feat: Enhance Markdown rendering with improved list, table, and inline code support; update tag display styling for better visual consistency

This commit is contained in:
Simon Larsen
2025-08-19 11:47:21 +01:00
parent 6060d66c2b
commit 3927bea29c
2 changed files with 46 additions and 7 deletions

View File

@@ -19,9 +19,8 @@ export default class Markdown {
markdown: string,
contentType: MarkdownContentType,
): Promise<string> {
// convert tags > and < to &gt; and &lt;
markdown = markdown.replace(/</g, "&lt;");
markdown = markdown.replace(/>/g, "&gt;");
// Basic sanitization: neutralize script tags but preserve markdown syntax like '>' for blockquotes.
markdown = markdown.replace(/<script/gi, "&lt;script");
let renderer: Renderer | null = null;
@@ -137,6 +136,46 @@ export default class Markdown {
return `<h6 class="my-5 tracking-tight font-bold text-gray-800">${text}</h6>`;
};
// Lists
renderer.list = function (body, ordered, start) {
const tag: string = ordered ? "ol" : "ul";
const cls: string = ordered
? "list-decimal pl-6 my-6 space-y-2 text-gray-700"
: "list-disc pl-6 my-6 space-y-2 text-gray-700";
const startAttr: string = ordered && start !== 1 ? ` start=\"${start}\"` : "";
return `<${tag}${startAttr} class="${cls}">${body}</${tag}>`;
};
renderer.listitem = function (text) {
return `<li class="leading-7">${text}</li>`;
};
// Tables
renderer.table = function (header, body) {
return `<div class="overflow-x-auto my-8"><table class="min-w-full border border-gray-200 text-sm text-left">
${header}${body}
</table></div>`;
};
renderer.tablerow = function (content) {
return `<tr class="border-b last:border-b-0">${content}</tr>`;
};
renderer.tablecell = function (content, flags) {
const type: string = flags.header ? "th" : "td";
const base = "px-4 py-2 border-r last:border-r-0 border-gray-200";
const align = flags.align ? ` text-${flags.align}` : "";
const weight = flags.header ? " font-semibold bg-gray-50" : "";
return `<${type} class="${base}${align}${weight}">${content}</${type}>`;
};
// Inline code
renderer.codespan = function (code) {
return `<code class="rounded-md bg-gray-100 px-1.5 py-0.5 text-sm text-pink-600">${code}</code>`;
};
// Images
renderer.image = function (href, _title, text) {
return `<figure class="my-8"><img src="${href}" alt="${text}" class="rounded-xl shadow-sm border border-gray-200" loading="lazy"/><figcaption class="mt-2 text-center text-sm text-gray-500">${text || ""}</figcaption></figure>`;
};
this.blogRenderer = renderer;
return renderer;

View File

@@ -1,16 +1,16 @@
<% if(blogPost.tags.length> 0){ %>
<div class="flex mt-10">
<div class="space-x-1">
<div class="flex mt-10 justify-center">
<div class="flex flex-wrap gap-2 justify-center">
<!-- Loop over blogPost.tags and show them here-->
<% for(var i=0; i<blogPost.tags.length; i++) {%>
<a href="/blog/tag/<%- blogPost.tags[i].replaceAll(' ','-').toLowerCase() -%>">
<div
class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5 text-sm">
class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm hover:border-indigo-400 hover:bg-indigo-50 transition">
<div class="absolute flex flex-shrink-0 items-center justify-center">
<div class="h-1.5 w-1.5 rounded-full bg-indigo-500" aria-hidden="true">
</div>
</div>
<div class="ml-3.5 font-medium text-gray-900">
<div class="ml-3.5 font-medium text-gray-700">
<%- blogPost.tags[i] -%>
</div>
</div>