diff --git a/src/lib/components/content/MarkdownRenderer.svelte b/src/lib/components/content/MarkdownRenderer.svelte
index a4aa061..7d27be4 100644
--- a/src/lib/components/content/MarkdownRenderer.svelte
+++ b/src/lib/components/content/MarkdownRenderer.svelte
@@ -194,10 +194,87 @@
emojiUrls = resolvedUrls;
}
- // Replace emoji shortcodes with images in text
+ // Replace emoji shortcodes with images in text, but skip code blocks
function replaceEmojis(text: string): string {
let processed = text;
+ // Find all code blocks (Markdown and AsciiDoc syntax)
+ const codeBlockRanges: Array<{ start: number; end: number }> = [];
+
+ // First, match AsciiDoc source blocks (---- ... ----)
+ // AsciiDoc source blocks can have attributes like [source, json] before the dashes
+ // Match: 4+ dashes on a line, content, then 4+ dashes on a line
+ // We'll match the dashes and then find the closing dashes
+ const asciidocSourceBlockPattern = /^----+$/gm;
+ const lines = text.split(/\r?\n/);
+ let inAsciidocBlock = false;
+ let blockStart = -1;
+ let lineIndex = 0;
+ let charIndex = 0;
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ const lineStart = charIndex;
+ const lineEnd = charIndex + line.length;
+
+ // Check if this line is 4+ dashes
+ if (/^----+$/.test(line.trim())) {
+ if (!inAsciidocBlock) {
+ // Starting a new block
+ inAsciidocBlock = true;
+ blockStart = lineStart;
+ } else {
+ // Ending a block
+ codeBlockRanges.push({ start: blockStart, end: lineEnd });
+ inAsciidocBlock = false;
+ blockStart = -1;
+ }
+ }
+
+ charIndex = lineEnd + 1; // +1 for newline
+ }
+
+ // If we're still in a block at the end, close it
+ if (inAsciidocBlock && blockStart >= 0) {
+ codeBlockRanges.push({ start: blockStart, end: text.length });
+ }
+
+ // Match Markdown/AsciiDoc fenced code blocks (```...```)
+ // Match triple backticks with optional language identifier
+ let match;
+ const fencedCodeBlockPattern = /```[a-zA-Z]*\n?[\s\S]*?```/g;
+ while ((match = fencedCodeBlockPattern.exec(text)) !== null) {
+ const start = match.index;
+ const end = start + match[0].length;
+ // Only add if not already inside an AsciiDoc source block
+ const isInsideAsciidoc = codeBlockRanges.some(range => start >= range.start && end <= range.end);
+ if (!isInsideAsciidoc) {
+ codeBlockRanges.push({ start, end });
+ }
+ }
+
+ // Then match inline code (`code`) - but exclude those already inside other blocks
+ // Match single backtick, but not if it's part of triple backticks
+ const inlineCodePattern = /`[^`\n]+`/g;
+ while ((match = inlineCodePattern.exec(text)) !== null) {
+ const start = match.index;
+ const end = start + match[0].length;
+
+ // Check if this inline code is already inside another code block
+ const isInsideBlock = codeBlockRanges.some(range => start >= range.start && end <= range.end);
+ if (!isInsideBlock) {
+ codeBlockRanges.push({ start, end });
+ }
+ }
+
+ // Sort ranges by start position
+ codeBlockRanges.sort((a, b) => a.start - b.start);
+
+ // Helper function to check if a position is inside a code block
+ function isInCodeBlock(index: number): boolean {
+ return codeBlockRanges.some(range => index >= range.start && index < range.end);
+ }
+
// Replace from end to start to preserve indices
const sortedEntries = Array.from(emojiUrls.entries()).sort((a, b) => {
const indexA = processed.lastIndexOf(a[0]);
@@ -210,7 +287,22 @@
const escapedShortcode = escapeHtml(shortcode);
// Replace with img tag, preserving the shortcode as alt text
const imgTag = ``;
- processed = processed.replaceAll(shortcode, imgTag);
+
+ // Find all occurrences and only replace those outside code blocks
+ let searchIndex = 0;
+ while (true) {
+ const index = processed.indexOf(shortcode, searchIndex);
+ if (index === -1) break;
+
+ if (!isInCodeBlock(index)) {
+ // Replace this occurrence
+ processed = processed.substring(0, index) + imgTag + processed.substring(index + shortcode.length);
+ searchIndex = index + imgTag.length;
+ } else {
+ // Skip this occurrence (it's in a code block)
+ searchIndex = index + shortcode.length;
+ }
+ }
}
return processed;
@@ -454,6 +546,32 @@
// Post-process to fix any greentext that markdown converted to blockquotes
html = postProcessGreentext(html);
+ // Remove emoji images that are inside code blocks (they should be plain text)
+ // This handles cases where emojis were replaced before markdown/AsciiDoc parsing
+ // Handle tags (inline code)
+ html = html.replace(/]*>([\s\S]*?)<\/code>/gi, (match, codeContent) => {
+ // Remove any emoji img tags inside code blocks and restore the original shortcode
+ return match.replace(/]*class="emoji-inline"[^>]*alt="([^"]*)"[^>]*>/gi, '$1');
+ });
+
+ // Handle
tags (code blocks and AsciiDoc source blocks)
+ html = html.replace(/
]*>([\s\S]*?)<\/pre>/gi, (match, preContent) => {
+ // Remove any emoji img tags inside pre blocks and restore the original shortcode
+ return match.replace(/]*class="emoji-inline"[^>]*alt="([^"]*)"[^>]*>/gi, '$1');
+ });
+
+ // Handle AsciiDoc source blocks (they use
with
inside)
+ html = html.replace(/
]*class="listingblock"[^>]*>([\s\S]*?)<\/div>/gi, (match, divContent) => {
+ // Remove emoji images from AsciiDoc listing blocks
+ return match.replace(/]*class="emoji-inline"[^>]*alt="([^"]*)"[^>]*>/gi, '$1');
+ });
+
+ // Also handle any other code-related elements that might contain emojis
+ html = html.replace(/<[^>]*class="[^"]*code[^"]*"[^>]*>([\s\S]*?)<\/[^>]+>/gi, (match, content) => {
+ // Remove emoji images from any element with "code" in its class
+ return match.replace(/]*class="emoji-inline"[^>]*alt="([^"]*)"[^>]*>/gi, '$1');
+ });
+
// Fix malformed image tags - ensure all img src attributes are absolute URLs
// This prevents the browser from trying to fetch markdown syntax or malformed tags as relative URLs
html = html.replace(/]*?)>/gi, (match, attributes) => {
diff --git a/src/lib/components/layout/Header.svelte b/src/lib/components/layout/Header.svelte
index 11d1de3..dda4ab1 100644
--- a/src/lib/components/layout/Header.svelte
+++ b/src/lib/components/layout/Header.svelte
@@ -47,6 +47,7 @@
{/if}
/Relay/Topics
+ /Repos/Cache
Comments ({issueComments.get(issue.id)!.length})
+ {#each issueComments.get(issue.id)! as comment} +