From c7621659aea49e778ab1fe2aead35ef140eb45a2 Mon Sep 17 00:00:00 2001 From: Syrup Date: Sun, 2 Nov 2025 12:29:39 +0000 Subject: [PATCH 1/9] fix: remove .html extension from URLs and add language labels to code blocks - Update vercel.json with rewrites to support clean URLs without .html - Add language label to code blocks positioned at top-left corner - Label stays fixed when scrolling code horizontally (sticky positioning) - Style: transparent background, lowercase text, subtle gray color - Support both light and dark mode --- index.js | 5 ++++- public/style.css | 33 +++++++++++++++++++++++++++++++++ vercel.json | 7 ++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index e3100b2..79ed7f8 100644 --- a/index.js +++ b/index.js @@ -27,7 +27,10 @@ const md = new MarkdownIt({ highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { - return '
' +
+        const langLabel = `${lang}`;
+        return `
` +
+               langLabel +
+               `` +
                hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
                '
'; } catch (__) {} diff --git a/public/style.css b/public/style.css index fb53ed7..7a929b1 100644 --- a/public/style.css +++ b/public/style.css @@ -51,6 +51,34 @@ pre.hljs { position: relative; } +pre.hljs code { + display: block; + position: relative; +} + +.code-lang { + position: sticky !important; + top: 0 !important; + left: 0 !important; + float: left !important; + background-color: transparent !important; + color: #888 !important; + padding: 2px 6px !important; + font-size: 9px !important; + font-family: 'Courier New', monospace !important; + font-weight: 600 !important; + line-height: 1 !important; + text-transform: none !important; + border-top-left-radius: 2px !important; + border-bottom-right-radius: 3px !important; + user-select: none !important; + pointer-events: none !important; + margin: 0 !important; + margin-bottom: -14px !important; + display: inline-block !important; + z-index: 1 !important; +} + .code-wrapper { position: relative; display: inline-block; @@ -264,6 +292,11 @@ blockquote.alert p:last-child { margin-bottom: 0; } +:root.dark .code-lang { + background-color: transparent !important; + color: #666 !important; +} + @media screen and (max-width: 600px) { pre.hljs, .code-wrapper { diff --git a/vercel.json b/vercel.json index 96f4b75..c1b0dcf 100644 --- a/vercel.json +++ b/vercel.json @@ -2,5 +2,10 @@ "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", - "installCommand": "npm install" + "installCommand": "npm install", + "cleanUrls": true, + "trailingSlash": false, + "rewrites": [ + { "source": "/:path*", "destination": "/:path*.html" } + ] } From 8295218e668d569fd0d923b6fc903c3d7876ff97 Mon Sep 17 00:00:00 2001 From: Fabian Maulana Date: Sun, 2 Nov 2025 19:33:40 +0700 Subject: [PATCH 2/9] Update vercel.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index c1b0dcf..54b3e50 100644 --- a/vercel.json +++ b/vercel.json @@ -6,6 +6,6 @@ "cleanUrls": true, "trailingSlash": false, "rewrites": [ - { "source": "/:path*", "destination": "/:path*.html" } + { "source": "/((?!.*\\.).*)", "destination": "/$1.html" } ] } From 2f237f316ad707ee63a940a165baaa5095bbbc5f Mon Sep 17 00:00:00 2001 From: Fabian Maulana Date: Sun, 2 Nov 2025 19:34:16 +0700 Subject: [PATCH 3/9] Update public/style.css Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- public/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/style.css b/public/style.css index 7a929b1..d68f8b8 100644 --- a/public/style.css +++ b/public/style.css @@ -60,7 +60,7 @@ pre.hljs code { position: sticky !important; top: 0 !important; left: 0 !important; - float: left !important; + /* float: left !important; */ /* Removed to fix sticky positioning */ background-color: transparent !important; color: #888 !important; padding: 2px 6px !important; From 2ed312d71c183b761495b824a15f8a6e1385ab1e Mon Sep 17 00:00:00 2001 From: Fabian Maulana Date: Sun, 2 Nov 2025 19:34:28 +0700 Subject: [PATCH 4/9] Update public/style.css Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- public/style.css | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/public/style.css b/public/style.css index d68f8b8..68d464f 100644 --- a/public/style.css +++ b/public/style.css @@ -56,27 +56,27 @@ pre.hljs code { position: relative; } -.code-lang { - position: sticky !important; - top: 0 !important; - left: 0 !important; - /* float: left !important; */ /* Removed to fix sticky positioning */ - background-color: transparent !important; - color: #888 !important; - padding: 2px 6px !important; - font-size: 9px !important; - font-family: 'Courier New', monospace !important; - font-weight: 600 !important; - line-height: 1 !important; - text-transform: none !important; - border-top-left-radius: 2px !important; - border-bottom-right-radius: 3px !important; - user-select: none !important; - pointer-events: none !important; - margin: 0 !important; - margin-bottom: -14px !important; - display: inline-block !important; - z-index: 1 !important; +.code-wrapper .code-lang { + position: sticky; + top: 0; + left: 0; + float: left; + background-color: transparent; + color: #888; + padding: 2px 6px; + font-size: 9px; + font-family: 'Courier New', monospace; + font-weight: 600; + line-height: 1; + text-transform: none; + border-top-left-radius: 2px; + border-bottom-right-radius: 3px; + user-select: none; + pointer-events: none; + margin: 0; + margin-bottom: -14px; + display: inline-block; + z-index: 1; } .code-wrapper { From 87a1f8497617fea032770639b6bb5417d6e22a00 Mon Sep 17 00:00:00 2001 From: Fabian Maulana Date: Sun, 2 Nov 2025 19:34:37 +0700 Subject: [PATCH 5/9] Update index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 79ed7f8..9ff9064 100644 --- a/index.js +++ b/index.js @@ -27,8 +27,8 @@ const md = new MarkdownIt({ highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { - const langLabel = `${lang}`; - return `
` +
+        const langLabel = `${md.utils.escapeHtml(lang)}`;
+        return `
` +
                langLabel +
                `` +
                hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +

From e24fea9202639e1fb73e0509bb76f4cb8369b762 Mon Sep 17 00:00:00 2001
From: Syrup 
Date: Sun, 2 Nov 2025 12:43:15 +0000
Subject: [PATCH 6/9] refactor: extract highlight function to DRY helper

- Create helpers/highlight.js for shared highlight logic
- Remove duplicate code from index.js and cli/build.js
- Follows DRY principle for better maintainability
---
 cli/build.js         | 13 ++-----------
 helpers/highlight.js | 15 +++++++++++++++
 index.js             | 16 ++--------------
 3 files changed, 19 insertions(+), 25 deletions(-)
 create mode 100644 helpers/highlight.js

diff --git a/cli/build.js b/cli/build.js
index b837fb5..d885248 100644
--- a/cli/build.js
+++ b/cli/build.js
@@ -2,13 +2,13 @@ import { readFileSync, writeFileSync, readdirSync, existsSync, rmSync, mkdirSync
 import { join, dirname } from 'node:path';
 import { glob } from 'glob';
 import frontMatter from 'front-matter';
-import hljs from 'highlight.js';
 import Handlebars from 'handlebars';
 import ejs from 'ejs';
 import MarkdownIt from 'markdown-it';
 import markdownItTaskLists from 'markdown-it-task-lists';
 import markdownItEmoji from 'markdown-it-emoji/light.js';
 import markdownItAlerts from '../helpers/markdown-it-alerts.js';
+import { highlightCode } from '../helpers/highlight.js';
 import { parse as parseToml } from 'toml';
 import '../helpers/index.js';
 
@@ -16,16 +16,7 @@ const configFile = readFileSync(join(process.cwd(), 'config.toml'), 'utf8');
 const config = parseToml(configFile);
 
 const md = new MarkdownIt({
-  highlight: function (str, lang) {
-    if (lang && hljs.getLanguage(lang)) {
-      try {
-        return '
' +
-               hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
-               '
'; - } catch (__) {} - } - return '
' + md.utils.escapeHtml(str) + '
'; - }, + highlight: highlightCode, html: true }); diff --git a/helpers/highlight.js b/helpers/highlight.js new file mode 100644 index 0000000..c9ad5f8 --- /dev/null +++ b/helpers/highlight.js @@ -0,0 +1,15 @@ +import hljs from 'highlight.js'; + +export function highlightCode(str, lang, md) { + if (lang && hljs.getLanguage(lang)) { + try { + const langLabel = `${md.utils.escapeHtml(lang)}`; + return `
` +
+             langLabel +
+             `` +
+             hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
+             '
'; + } catch (__) {} + } + return '
' + md.utils.escapeHtml(str) + '
'; +} diff --git a/index.js b/index.js index 9ff9064..c88c2bb 100644 --- a/index.js +++ b/index.js @@ -3,13 +3,13 @@ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { glob } from 'glob'; import frontMatter from 'front-matter'; -import hljs from 'highlight.js'; import express from 'express'; import Handlebars from 'handlebars'; import MarkdownIt from 'markdown-it'; import markdownItTaskLists from 'markdown-it-task-lists'; import markdownItEmoji from 'markdown-it-emoji/light.js'; import markdownItAlerts from './helpers/markdown-it-alerts.js'; +import { highlightCode } from './helpers/highlight.js'; import { parse as parseToml } from 'toml'; import './helpers/index.js'; @@ -24,19 +24,7 @@ const config = parseToml(configFile); // Setup markdown-it const md = new MarkdownIt({ - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - const langLabel = `${md.utils.escapeHtml(lang)}`; - return `
` +
-               langLabel +
-               `` +
-               hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
-               '
'; - } catch (__) {} - } - return '
' + md.utils.escapeHtml(str) + '
'; - }, + highlight: highlightCode, html: true }); From 4e78d8a0387b71876bb61cf64376573ecabbd5c0 Mon Sep 17 00:00:00 2001 From: Syrup Date: Sun, 2 Nov 2025 12:54:47 +0000 Subject: [PATCH 7/9] fix: use MarkdownIt utils instance for escapeHtml - Create mdUtils instance to access escapeHtml independently - Fixes build error where md parameter was undefined - Removes md parameter from highlightCode function signature --- helpers/highlight.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/helpers/highlight.js b/helpers/highlight.js index c9ad5f8..e5e63d1 100644 --- a/helpers/highlight.js +++ b/helpers/highlight.js @@ -1,15 +1,18 @@ import hljs from 'highlight.js'; +import MarkdownIt from 'markdown-it'; -export function highlightCode(str, lang, md) { +const mdUtils = MarkdownIt().utils; + +export function highlightCode(str, lang) { if (lang && hljs.getLanguage(lang)) { try { - const langLabel = `${md.utils.escapeHtml(lang)}`; - return `
` +
+      const langLabel = `${mdUtils.escapeHtml(lang)}`;
+      return `
` +
              langLabel +
              `` +
              hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
              '
'; } catch (__) {} } - return '
' + md.utils.escapeHtml(str) + '
'; + return '
' + mdUtils.escapeHtml(str) + '
'; } From 0b42b412f99ebfa78cbffbdd3c2bb5ac2dab3094 Mon Sep 17 00:00:00 2001 From: Fabian Maulana Date: Sun, 2 Nov 2025 20:01:04 +0700 Subject: [PATCH 8/9] Update helpers/highlight.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- helpers/highlight.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/highlight.js b/helpers/highlight.js index e5e63d1..cfef2f4 100644 --- a/helpers/highlight.js +++ b/helpers/highlight.js @@ -7,8 +7,8 @@ export function highlightCode(str, lang) { if (lang && hljs.getLanguage(lang)) { try { const langLabel = `${mdUtils.escapeHtml(lang)}`; - return `
` +
-             langLabel +
+      return langLabel +
+             `
` +
              `` +
              hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
              '
'; From e1bf2f7814efab1aaeace5d9087ad68baddf6f85 Mon Sep 17 00:00:00 2001 From: Fabian Maulana Date: Sun, 2 Nov 2025 20:01:15 +0700 Subject: [PATCH 9/9] Update public/style.css Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- public/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/style.css b/public/style.css index 68d464f..e20fee8 100644 --- a/public/style.css +++ b/public/style.css @@ -56,7 +56,7 @@ pre.hljs code { position: relative; } -.code-wrapper .code-lang { +pre.hljs .code-lang { position: sticky; top: 0; left: 0;