refactor: remove blog and markdown test pages, update navigation links

- Deleted blog.html and associated blog post files (markdown-test, testddd, node-graphs, welcome).
- Updated index.html to use dynamic navigation links from navLinks.js.
- Enhanced MarkdownRenderer to include new heading pin elements.
- Implemented SVG bezier splines for blog post navigation.
- Adjusted CSS styles for blog layout and new elements.
This commit is contained in:
2026-05-10 23:29:02 +10:00
parent fabbb0539a
commit ba36cdd05b
13 changed files with 446 additions and 340 deletions

View File

@@ -97,14 +97,17 @@ export class MarkdownRenderer {
// Horizontal rule
if (/^[-*_]{3,}$/.test(block)) {
return `<hr class="md-hr" />`;
return `<div class="md-hr" role="separator"><span class="md-hr-line"></span><span class="md-hr-arrow"></span></div>`;
}
// Headings
const headingMatch = block.match(/^(#{1,6})\s+(.+)$/m);
if (headingMatch && block.split("\n").length === 1) {
const level = headingMatch[1].length;
return `<h${level} class="md-h${level}">${MarkdownRenderer.#renderInline(headingMatch[2])}</h${level}>`;
const pin = level === 1 ? '<span class="md-h1-pin" aria-hidden="true"></span>'
: level === 2 ? '<span class="md-h2-pin" aria-hidden="true"></span>'
: '';
return `<h${level} class="md-h${level}">${pin}${MarkdownRenderer.#renderInline(headingMatch[2])}</h${level}>`;
}
// Tables

View File

@@ -95,6 +95,106 @@ function setupScrollTopBar() {
}
}
/**
* Draws SVG bezier splines from the peek-card exec pins to the main blog card.
*
* @returns {void}
*/
function setupPeekSplines() {
const svg = document.querySelector(".blog-post-splines");
const layout = document.querySelector(".blog-post-layout");
const card = document.querySelector(".blog-card");
if (!svg || !layout || !card) return;
const WIRE_COLOR = "#ffffff";
const WIRE_OPACITY = "0.35";
/**
* Creates an SVG path element styled as an exec wire.
*
* @returns {SVGPathElement}
*/
function makePath() {
const p = document.createElementNS("http://www.w3.org/2000/svg", "path");
p.setAttribute("fill", "none");
p.setAttribute("stroke", WIRE_COLOR);
p.setAttribute("stroke-width", "2.5");
p.setAttribute("stroke-linecap", "round");
p.style.opacity = WIRE_OPACITY;
return p;
}
/**
* Returns the center of an element relative to the layout container.
*
* @param {Element} el
* @returns {{x: number, y: number}}
*/
function centerOf(el) {
const er = el.getBoundingClientRect();
const lr = layout.getBoundingClientRect();
return {
x: er.left - lr.left + er.width / 2,
y: er.top - lr.top + er.height / 2,
};
}
/**
* Returns the midpoint of a card's left or right edge relative to the layout.
*
* @param {Element} el
* @param {'left'|'right'} side
* @returns {{x: number, y: number}}
*/
function edgeOf(el, side) {
const er = el.getBoundingClientRect();
const lr = layout.getBoundingClientRect();
return {
x: side === 'left' ? er.left - lr.left : er.right - lr.left,
y: er.top - lr.top + 158,
};
}
const pathPrev = makePath();
const pathNext = makePath();
svg.appendChild(pathPrev);
svg.appendChild(pathNext);
/** @returns {void} */
function draw() {
const lr = layout.getBoundingClientRect();
svg.setAttribute("width", String(lr.width));
svg.setAttribute("height", String(lr.height));
const prevPin = document.querySelector("[data-peek-pin='prev-out']");
const nextPin = document.querySelector("[data-peek-pin='next-in']");
const cardLeft = edgeOf(card, "left");
const cardRight = edgeOf(card, "right");
if (prevPin) {
const s = centerOf(prevPin);
const e = cardLeft;
const cp = Math.max(60, Math.abs(e.x - s.x) * 0.5);
pathPrev.setAttribute("d", `M ${s.x} ${s.y} C ${s.x + cp} ${s.y} ${e.x - cp} ${e.y} ${e.x} ${e.y}`);
} else {
pathPrev.setAttribute("d", "");
}
if (nextPin) {
const s = cardRight;
const e = centerOf(nextPin);
const cp = Math.max(60, Math.abs(e.x - s.x) * 0.5);
pathNext.setAttribute("d", `M ${s.x} ${s.y} C ${s.x + cp} ${s.y} ${e.x - cp} ${e.y} ${e.x} ${e.y}`);
} else {
pathNext.setAttribute("d", "");
}
}
draw();
window.addEventListener("resize", draw, { passive: true });
window.addEventListener("scroll", draw, { passive: true });
}
/**
* Bootstraps static blog page behaviors.
*
@@ -103,6 +203,7 @@ function setupScrollTopBar() {
function initializeBlogPage() {
renderStaticBlogPost();
setupScrollTopBar();
setupPeekSplines();
}
if (document.readyState === "loading") {