From e10bdf88dc0a82beb721725c2e956febfb2c4796 Mon Sep 17 00:00:00 2001 From: Markus Graf Date: Sun, 29 Mar 2026 21:25:03 +0200 Subject: [PATCH] feat(quick-260329-ton): implement renderInlineMarkdown utility - Escapes HTML special chars first (XSS safe) - Converts **bold** to , *italic* to - Standalone asterisks with spaces are left unchanged - Pure function, no DOM dependency --- src/utils/markdown.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/utils/markdown.ts diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts new file mode 100644 index 0000000..dd41715 --- /dev/null +++ b/src/utils/markdown.ts @@ -0,0 +1,30 @@ +/** + * Renders inline Markdown (bold, italic) to HTML safely. + * + * - Escapes HTML special characters first to prevent XSS + * - Converts **bold** to bold + * - Converts *italic* to italic + * - Standalone asterisks surrounded by spaces are left unchanged + * + * Pure function — no DOM required. + */ +export function renderInlineMarkdown(text: string): string { + if (text === "") return ""; + + // Step 1: Escape HTML special characters to prevent XSS + let result = text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + + // Step 2: Convert **bold** (must come before *italic* so ** is not consumed as two *) + result = result.replace(/\*\*(.+?)\*\*/g, "$1"); + + // Step 3: Convert *italic* — only matches when content is non-empty and + // asterisks are not surrounded by spaces (avoids "2 * 3 * 4" false positives) + result = result.replace(/\*(\S[^*]*?\S|\S)\*/g, "$1"); + + return result; +}