import React from 'react';
import { open } from '@tauri-apps/plugin-shell';
/**
* Render inline markdown segments: **bold**, *italic*, `code`, [text](url).
* External links open in the user's default browser via the Tauri shell plugin.
*/
export function renderInlineMarkdown(text: string, keyPrefix = 'i'): React.ReactNode[] {
// Tokenize — order matters: links first (no recursion), then emphasis/code.
const tokens: React.ReactNode[] = [];
const linkRe = /\[([^\]]+)\]\(([^)]+)\)/g;
let lastIndex = 0;
let match: RegExpExecArray | null;
let i = 0;
const pushInline = (segment: string) => {
const parts = segment.split(/(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g);
for (const part of parts) {
if (!part) continue;
if (part.startsWith('**') && part.endsWith('**')) {
tokens.push({part.slice(2, -2)});
} else if (part.startsWith('*') && part.endsWith('*') && part.length > 2) {
tokens.push({part.slice(1, -1)});
} else if (part.startsWith('`') && part.endsWith('`')) {
tokens.push({part.slice(1, -1)});
} else {
tokens.push(part);
}
}
};
while ((match = linkRe.exec(text)) !== null) {
if (match.index > lastIndex) pushInline(text.slice(lastIndex, match.index));
const [full, label, url] = match;
tokens.push(
{ e.preventDefault(); open(url).catch(() => {}); }}
className="whats-new-link"
>
{label}
);
lastIndex = match.index + full.length;
}
if (lastIndex < text.length) pushInline(text.slice(lastIndex));
return tokens;
}
/**
* Render a subset of GitHub-flavored Markdown used by our CHANGELOG: headings
* (### / ####), bullets (- / *), blockquotes, horizontal rules, and inline
* formatting (bold/italic/code/links).
*/
export function renderChangelogBody(body: string): React.ReactNode[] {
const lines = body.split('\n');
const out: React.ReactNode[] = [];
let bulletBuffer: React.ReactNode[] = [];
let quoteBuffer: string[] = [];
const flushBullets = () => {
if (bulletBuffer.length === 0) return;
out.push(
{renderInlineMarkdown(quoteBuffer.join(' '), `q-${out.length}`)}); quoteBuffer = []; }; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const trimmed = line.trim(); if (trimmed === '') { flushBullets(); flushQuote(); continue; } if (trimmed === '---') { flushBullets(); flushQuote(); out.push(
{renderInlineMarkdown(line, `p-${i}`)}
); } flushBullets(); flushQuote(); return out; }