feat: enhance blog functionality with markdown rendering and bundling

- Updated build process to bundle main and blog scripts separately using esbuild.
- Modified HTML files to reference new bundled JavaScript files for improved performance.
- Implemented a new blogPage.js script to handle markdown rendering and copy functionality.
- Replaced inline markdown rendering with JSON payloads for better separation of content and presentation.
- Added Prism.js for syntax highlighting in blog posts.
- Improved CSS styles for better layout and scrolling behavior on blog pages.
This commit is contained in:
2026-05-10 19:53:19 +10:00
parent 35b049b833
commit a16445dd78
9 changed files with 192 additions and 309 deletions

View File

@@ -0,0 +1,72 @@
import { MarkdownRenderer } from "./MarkdownRenderer.js";
/**
* Decodes markdown payload from a JSON script element.
*
* @param {HTMLScriptElement | null} payloadEl
* @returns {string}
*/
function readMarkdownPayload(payloadEl) {
if (!payloadEl) return "";
try {
const parsed = JSON.parse(payloadEl.textContent || "\"\"");
return typeof parsed === "string" ? parsed : "";
} catch (error) {
console.error("Failed to parse blog markdown payload", error);
return "";
}
}
/**
* Wires copy buttons rendered by MarkdownRenderer.
*
* @param {ParentNode} container
* @returns {void}
*/
function wireCopyButtons(container) {
container.querySelectorAll(".md-copy-btn").forEach((button) => {
button.addEventListener("click", (event) => {
event.stopPropagation();
const code = button.getAttribute("data-code");
if (!code) return;
navigator.clipboard.writeText(code).then(() => {
const originalText = button.textContent;
button.textContent = "✓";
button.classList.add("copied");
setTimeout(() => {
button.textContent = originalText;
button.classList.remove("copied");
}, 2000);
}).catch((error) => {
console.error("Failed to copy code", error);
});
});
});
}
/**
* Renders blog markdown on static post pages using the shared renderer.
*
* @returns {void}
*/
function renderStaticBlogPost() {
/** @type {HTMLElement | null} */
const target = document.querySelector("[data-blog-post-content]");
if (!target) return;
/** @type {HTMLScriptElement | null} */
const payload = document.querySelector("#blogPostMarkdown");
const markdown = readMarkdownPayload(payload);
target.innerHTML = MarkdownRenderer.render(markdown);
MarkdownRenderer.applyImageScale(target);
wireCopyButtons(target);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", renderStaticBlogPost, { once: true });
} else {
renderStaticBlogPost();
}