diff --git a/public/healthz.json b/public/healthz.json index 5be6279..119d3cb 100644 --- a/public/healthz.json +++ b/public/healthz.json @@ -2,7 +2,7 @@ "status": "ok", "service": "aitherboard", "version": "0.1.1", - "buildTime": "2026-02-05T18:24:55.668Z", + "buildTime": "2026-02-05T22:42:01.031Z", "gitCommit": "unknown", - "timestamp": 1770315895668 + "timestamp": 1770331321031 } \ No newline at end of file diff --git a/src/lib/components/content/MarkdownRenderer.svelte b/src/lib/components/content/MarkdownRenderer.svelte index 901b9b9..472d2dc 100644 --- a/src/lib/components/content/MarkdownRenderer.svelte +++ b/src/lib/components/content/MarkdownRenderer.svelte @@ -331,26 +331,42 @@ } // Convert greentext (>text with no space) to styled spans + // Groups consecutive greentext lines into a single block, preserving line breaks function convertGreentext(text: string): string { - // Split by lines and process each line + // Split by lines and process const lines = text.split('\n'); - const processedLines = lines.map(line => { - // Check if line starts with > followed immediately by non-whitespace (greentext) - // Must match: >text (no space after >) - // Must NOT match: > text (space after >, normal blockquote) - // Also handle HTML-escaped > (>) - const greentextPattern = /^(>|>)([^\s>].*)$/; + const processedLines: string[] = []; + let greentextBlock: string[] = []; + + const greentextPattern = /^(>|>)([^\s>].*)$/; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; const match = line.match(greentextPattern); if (match) { - // This is greentext - wrap in span with greentext class - // Use > character (not >) since we're inserting HTML + // This is greentext - add to current block const greentextContent = escapeHtml(match[2]); - return `>${greentextContent}`; + greentextBlock.push(greentextContent); + } else { + // Not greentext - flush any accumulated greentext block + if (greentextBlock.length > 0) { + // Join with
to preserve line breaks, no extra spacing + const blockContent = greentextBlock.map(content => `>${content}`).join('
'); + processedLines.push(`${blockContent}`); + greentextBlock = []; + } + // Add the non-greentext line as-is + processedLines.push(line); } - - return line; - }); + } + + // Flush any remaining greentext block at the end + if (greentextBlock.length > 0) { + // Join with
to preserve line breaks, no extra spacing + const blockContent = greentextBlock.map(content => `>${content}`).join('
'); + processedLines.push(`${blockContent}`); + } return processedLines.join('\n'); } @@ -507,16 +523,30 @@ }); // Post-process HTML to convert blockquotes that are actually greentext + // Merges consecutive greentext blockquotes into a single block, preserving line breaks function postProcessGreentext(html: string): string { - // Find blockquotes that match greentext pattern (>text with no space) - // These are blockquotes that markdown created from greentext lines - // Pattern:

>text

where there's no space after > - const greentextBlockquotePattern = /]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>/g; + // Pattern to match one or more consecutive greentext blockquotes + // Matches:

>text

(with optional whitespace between) + // where there's no space after > (greentext pattern) + // The (?:...) part matches zero or more additional consecutive blockquotes + const consecutiveGreentextPattern = /(]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>)(\s*]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>)*/g; - return html.replace(greentextBlockquotePattern, (match, content) => { - // Convert to greentext span - const escapedContent = escapeHtml(content); - return `>${escapedContent}`; + return html.replace(consecutiveGreentextPattern, (match) => { + // Extract all greentext contents from the match + const contentPattern = /]*>\s*]*>>([^\s<].*?)<\/p>\s*<\/blockquote>/g; + const contents: string[] = []; + let contentMatch; + while ((contentMatch = contentPattern.exec(match)) !== null) { + contents.push(contentMatch[1]); + } + + if (contents.length === 0) { + return match; // Shouldn't happen, but safety check + } + + // Join all contents with
to preserve line breaks, no extra spacing + const combinedContent = contents.map(c => escapeHtml(c)).map(content => `>${content}`).join('
'); + return `${combinedContent}`; }); } diff --git a/src/lib/components/content/MediaViewer.svelte b/src/lib/components/content/MediaViewer.svelte index 801d1e9..668081f 100644 --- a/src/lib/components/content/MediaViewer.svelte +++ b/src/lib/components/content/MediaViewer.svelte @@ -45,9 +45,11 @@ {#if mediaType === 'image'} Media {:else if mediaType === 'video'} -