]> juplo.de Git - website/commitdiff
Fix: <code>-Tags wurden in APT-Code-Blöcken als Text angezeigt
authorKai Moritz <kai.milan.moritz@googlemail.com>
Sun, 12 Jul 2026 08:47:00 +0000 (08:47 +0000)
committerKai Moritz <kai@juplo.de>
Sun, 12 Jul 2026 10:27:24 +0000 (12:27 +0200)
Pass 2 in highlightBody matchte <pre>...</pre> und übergab dabei den
<code>...</code>-Wrapper ungefiltert als Code-Inhalt an Shiki. APT-generierte
Blöcke haben die Struktur <pre><code>...</code></pre> (ohne Klasse), weshalb
<code> und </code> im gerenderten Output sichtbar wurden.

Fix: Neuer Pass 2 behandelt <pre><code>...</code></pre> ohne Klasse explizit.
Pass 3 bleibt für bare <pre>-Blöcke. Gemeinsame Detektionslogik in
detectAndHighlight() extrahiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
src/components/ProjectPage.astro

index e723a128f766fd457b3d9ea2f84405e7aa38db99..ba14e397fcd1927a29c15661a89578b7a2adc554 100644 (file)
@@ -44,8 +44,20 @@ async function toShiki(code: string, lang: string): Promise<string> {
   }
 }
 
+async function detectAndHighlight(rawCode: string): Promise<string> {
+  const code = decodeEntities(rawCode).trimEnd();
+  const detected = hljs.highlightAuto(code);
+  let lang = 'text';
+  if (detected.language && detected.relevance >= MIN_RELEVANCE && detected.language in bundledLanguages) {
+    lang = detected.language;
+  } else if (code.trimStart().startsWith('<')) {
+    lang = 'xml';
+  }
+  return toShiki(code, lang);
+}
+
 async function highlightBody(html: string): Promise<string> {
-  // Pass 1: labeled Markdown blocks — <pre><code class="language-LANG">...</code></pre>
+  // Pass 1: labeled blocks — <pre><code class="language-LANG">...</code></pre>
   html = await replaceAsync(
     html,
     /<pre><code class="language-([^"]+)">([\s\S]*?)<\/code><\/pre>/g,
@@ -59,21 +71,18 @@ async function highlightBody(html: string): Promise<string> {
     }
   );
 
-  // Pass 2: unlabeled APT blocks — <pre>...</pre> (no attributes)
+  // Pass 2: unlabeled blocks with code wrapper — <pre><code>...</code></pre>
+  html = await replaceAsync(
+    html,
+    /<pre><code>([\s\S]*?)<\/code><\/pre>/g,
+    async (_, rawCode) => detectAndHighlight(rawCode)
+  );
+
+  // Pass 3: plain APT blocks — <pre>...</pre> (no attributes, no code wrapper)
   html = await replaceAsync(
     html,
     /<pre>([\s\S]*?)<\/pre>/g,
-    async (_, rawCode) => {
-      const code = decodeEntities(rawCode).trimEnd();
-      const detected = hljs.highlightAuto(code);
-      let lang = 'text';
-      if (detected.language && detected.relevance >= MIN_RELEVANCE && detected.language in bundledLanguages) {
-        lang = detected.language;
-      } else if (code.trimStart().startsWith('<')) {
-        lang = 'xml';
-      }
-      return toShiki(code, lang);
-    }
+    async (_, rawCode) => detectAndHighlight(rawCode)
   );
 
   return html;