diff --git a/TODO.md b/TODO.md index b28a089..fc1fe13 100644 --- a/TODO.md +++ b/TODO.md @@ -117,10 +117,13 @@ approved plan for full rationale on each decision referenced below. - [x] RSS/JSON feeds — `/rss.xml`, `/feed.json` - [x] Sitemap + robots.txt — `@astrojs/sitemap`, static `public/robots.txt` - [x] Code block syntax highlighting — `toml-content-loader.ts` renders - fenced code blocks through shiki (`github-light`/`github-dark`, - `defaultColor: false` so only per-token colors are set and our own - `pre` background/border/padding still apply), switched by the same - `[data-theme]`/`prefers-color-scheme` pattern as `tokens.css` + fenced code blocks through `highlight.js`'s core build with only the + 11 languages content actually uses registered (no bundled-theme/WASM + grammar weight like shiki, which was tried first and swapped out for + this lighter option); `.hljs-*` token classes in `global.css` are + colored from the existing design tokens (`--accent`, `--text-muted`, + ...), so they already switch with the site's theme with no extra + `[data-theme]` rules needed ## Search & comments diff --git a/apps/site/package.json b/apps/site/package.json index a69fa65..b10c922 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -21,9 +21,9 @@ "@fontsource-variable/newsreader": "^5.3.0", "@fontsource/jetbrains-mono": "^5.3.0", "astro": "^7.2.9", + "highlight.js": "^11.12.0", "marked": "^18.0.11", "pagefind": "^1.5.2", - "shiki": "^4.4.3", "smol-toml": "^1.8.0", "three": "^0.185.1" }, diff --git a/apps/site/src/lib/toml-content-loader.ts b/apps/site/src/lib/toml-content-loader.ts index fc1abf1..51145a2 100644 --- a/apps/site/src/lib/toml-content-loader.ts +++ b/apps/site/src/lib/toml-content-loader.ts @@ -1,8 +1,19 @@ import { readdirSync, readFileSync, statSync } from "node:fs"; import { extname, join, relative } from "node:path"; import type { Loader } from "astro/loaders"; +import hljs from "highlight.js/lib/core"; +import hljsBash from "highlight.js/lib/languages/bash"; +import hljsC from "highlight.js/lib/languages/c"; +import hljsCss from "highlight.js/lib/languages/css"; +import hljsDos from "highlight.js/lib/languages/dos"; +import hljsIni from "highlight.js/lib/languages/ini"; +import hljsJavascript from "highlight.js/lib/languages/javascript"; +import hljsPowershell from "highlight.js/lib/languages/powershell"; +import hljsPython from "highlight.js/lib/languages/python"; +import hljsX86asm from "highlight.js/lib/languages/x86asm"; +import hljsXml from "highlight.js/lib/languages/xml"; +import hljsYaml from "highlight.js/lib/languages/yaml"; import { Marked } from "marked"; -import { createHighlighter } from "shiki"; import { parse as parseToml } from "smol-toml"; const FRONTMATTER_RE = /^\+\+\+\r?\n([\s\S]*?)\r?\n\+\+\+\r?\n?([\s\S]*)$/; @@ -34,50 +45,50 @@ function stripInlineMarkdown(text: string): string { // as `headings`, matching the shape a built-in markdown loader would give. let currentHeadings: Heading[] = []; -// Languages actually used in fenced code blocks across content/ — shiki -// resolves common aliases (js→javascript, sh/zsh→bash, ps1→powershell, -// bat→batch) on its own, so only the langs with no bundled grammar -// (gdb console sessions, svg markup, plain txt) need an explicit remap. -const highlighter = await createHighlighter({ - themes: ["github-light", "github-dark"], - langs: [ - "asm", - "bash", - "batch", - "c", - "css", - "html", - "javascript", - "powershell", - "python", - "toml", - "xml", - "yaml", - "plaintext", - ], -}); +// highlight.js's core build (no bundled grammars) plus only the languages +// actually used in content/'s fenced code blocks — keeps the highlighter +// thin instead of shipping its full ~190-language bundle. Aliases below +// map every fence lang seen in content to one of these registered names. +hljs.registerLanguage("bash", hljsBash); +hljs.registerLanguage("c", hljsC); +hljs.registerLanguage("css", hljsCss); +hljs.registerLanguage("dos", hljsDos); +hljs.registerLanguage("ini", hljsIni); +hljs.registerLanguage("javascript", hljsJavascript); +hljs.registerLanguage("powershell", hljsPowershell); +hljs.registerLanguage("python", hljsPython); +hljs.registerLanguage("x86asm", hljsX86asm); +hljs.registerLanguage("xml", hljsXml); +hljs.registerLanguage("yaml", hljsYaml); + const LANG_ALIASES: Record = { + asm: "x86asm", + bat: "dos", gdb: "plaintext", + html: "xml", + js: "javascript", + ps1: "powershell", + sh: "bash", svg: "xml", + toml: "ini", txt: "plaintext", + zsh: "bash", "": "plaintext", }; +function escapeHtml(text: string): string { + return text + .replace(/&/g, "&") + .replace(//g, ">"); +} + function highlightCode(code: string, lang: string | undefined): string { const resolved = LANG_ALIASES[lang ?? ""] ?? lang ?? "plaintext"; - try { - return highlighter.codeToHtml(code, { - lang: resolved, - themes: { light: "github-light", dark: "github-dark" }, - defaultColor: false, - }); - } catch { - return highlighter.codeToHtml(code, { - lang: "plaintext", - themes: { light: "github-light", dark: "github-dark" }, - defaultColor: false, - }); - } + const inner = hljs.getLanguage(resolved) + ? hljs.highlight(code, { language: resolved }).value + : escapeHtml(code); + return `
${inner}
`; } const marked = new Marked({ diff --git a/apps/site/src/styles/global.css b/apps/site/src/styles/global.css index b127f4c..7fd6e2e 100644 --- a/apps/site/src/styles/global.css +++ b/apps/site/src/styles/global.css @@ -93,26 +93,56 @@ pre { border: 1px solid var(--border); } -/* Shiki syntax highlighting (toml-content-loader.ts renders code fences with - defaultColor: false, so only per-token color vars are set — background, - padding, etc. stay on our own `pre` rule above). Follows the same - light/dark switching pattern as tokens.css: system preference by default, - [data-theme] overrides it either way. */ -.shiki, -.shiki span { - color: var(--shiki-light) !important; +/* highlight.js syntax highlighting (toml-content-loader.ts renders code + fences through hljs's core build). Colors are drawn from the existing + design tokens, so they already switch with the site's light/dark theme + without any extra [data-theme] rules here — see tokens.css. */ +.hljs-comment, +.hljs-quote { + color: var(--text-muted); + font-style: italic; } -@media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) .shiki, - :root:not([data-theme="light"]) .shiki span { - color: var(--shiki-dark) !important; - } +.hljs-keyword, +.hljs-selector-tag, +.hljs-built_in, +.hljs-type, +.hljs-literal, +.hljs-link { + color: var(--accent); } -:root[data-theme="dark"] .shiki, -:root[data-theme="dark"] .shiki span { - color: var(--shiki-dark) !important; +.hljs-string, +.hljs-attr, +.hljs-symbol, +.hljs-addition { + color: #4a9c6d; +} + +.hljs-number, +.hljs-meta, +.hljs-doctag { + color: #d1a53d; +} + +.hljs-title, +.hljs-title.function_, +.hljs-title.class_, +.hljs-name { + color: var(--text); + font-weight: 600; +} + +.hljs-deletion { + color: #d1502f; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: 700; } .container { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c85b86..6697496 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,15 +99,15 @@ importers: astro: specifier: ^7.2.9 version: 7.2.9(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@20.19.43)(terser@5.51.2)(yaml@2.9.0) + highlight.js: + specifier: ^11.12.0 + version: 11.12.0 marked: specifier: ^18.0.11 version: 18.0.11 pagefind: specifier: ^1.5.2 version: 1.5.2 - shiki: - specifier: ^4.4.3 - version: 4.4.3 smol-toml: specifier: ^1.8.0 version: 1.8.0 @@ -4779,6 +4779,10 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + highlight.js@11.12.0: + resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==} + engines: {node: '>=12.0.0'} + hls.js@1.5.20: resolution: {integrity: sha512-uu0VXUK52JhihhnN/MVVo1lvqNNuhoxkonqgO3IpjvQiGpJBdIXMGkofjQb/j9zvV7a1SW8U9g1FslWx/1HOiQ==} @@ -13590,6 +13594,8 @@ snapshots: highlight.js@10.7.3: {} + highlight.js@11.12.0: {} + hls.js@1.5.20: {} hoist-non-react-statics@3.3.2: