101 lines
4.1 KiB
TypeScript
101 lines
4.1 KiB
TypeScript
/**
|
|
* Markdown & Branded HTML Formatting Utilities for PostFundManagement (PFM)
|
|
*/
|
|
|
|
export function isHtmlContent(content: string): boolean {
|
|
if (!content) return false;
|
|
const trimmed = content.trim();
|
|
return (
|
|
trimmed.startsWith('<!DOCTYPE') ||
|
|
trimmed.startsWith('<html') ||
|
|
trimmed.startsWith('<div') ||
|
|
trimmed.startsWith('<table') ||
|
|
trimmed.startsWith('<p') ||
|
|
trimmed.startsWith('<h') ||
|
|
trimmed.startsWith('<ul') ||
|
|
trimmed.startsWith('<ol') ||
|
|
trimmed.startsWith('<blockquote') ||
|
|
(trimmed.includes('</') && trimmed.includes('>')) ||
|
|
/<(strong|b|em|i|u|h[1-6]|ul|ol|li|table|blockquote|div|p|img|hr|br)\b/i.test(trimmed)
|
|
);
|
|
}
|
|
|
|
export function stripMarkdown(text: string): string {
|
|
if (!text) return '';
|
|
return text
|
|
.replace(/<[^>]*>/g, ' ')
|
|
.replace(/!\[.*?\]\(.*?\)/g, '')
|
|
.replace(/\[(.*?)\]\(.*?\)/g, '$1')
|
|
.replace(/#{1,6}\s+/g, '')
|
|
.replace(/(\*\*|__)(.*?)\1/g, '$2')
|
|
.replace(/(\*|_)(.*?)\1/g, '$2')
|
|
.replace(/~~(.*?)~~/g, '$1')
|
|
.replace(/`{1,3}.*?`{1,3}/gs, '')
|
|
.replace(/^>\s+/gm, '')
|
|
.replace(/^[-*+]\s+/gm, '')
|
|
.replace(/^\d+\.\s+/gm, '')
|
|
.replace(/\|/g, ' ')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
export function parseMarkdownToHtml(markdown: string): string {
|
|
if (!markdown) return '';
|
|
|
|
let html = markdown
|
|
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
|
.replace(/\r\n/g, '\n');
|
|
|
|
html = html.replace(/```([a-zA-Z0-9_-]*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
|
|
const escaped = code
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>');
|
|
return `<pre style="background:#0b1120;border:1px solid #1e293b;border-radius:12px;padding:14px;overflow-x:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;color:#e2e8f0;margin:12px 0;"><code>${escaped}</code></pre>`;
|
|
});
|
|
|
|
html = html.replace(/`([^`]+)`/g, (_match, code) => {
|
|
const escaped = code
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>');
|
|
return `<code style="background:#1e293b;color:#34d399;padding:2px 6px;border-radius:6px;font-family:monospace;font-size:12px;">${escaped}</code>`;
|
|
});
|
|
|
|
html = html.replace(/^#### (.*?)$/gm, '<h4 style="font-size:14px;font-weight:700;color:#f8fafc;margin:12px 0 6px 0;">$1</h4>');
|
|
html = html.replace(/^### (.*?)$/gm, '<h3 style="font-size:16px;font-weight:700;color:#f8fafc;margin:14px 0 8px 0;">$1</h3>');
|
|
html = html.replace(/^## (.*?)$/gm, '<h2 style="font-size:18px;font-weight:800;color:#ffffff;margin:16px 0 8px 0;border-bottom:1px solid #1e293b;padding-bottom:6px;">$1</h2>');
|
|
html = html.replace(/^# (.*?)$/gm, '<h1 style="font-size:22px;font-weight:800;color:#ffffff;margin:18px 0 10px 0;border-bottom:1px solid #334155;padding-bottom:8px;">$1</h1>');
|
|
|
|
html = html.replace(/\*\*\*(.*?)\*\*\*/g, '<strong><em>$1</em></strong>');
|
|
html = html.replace(/\*\*(.*?)\*\*/g, '<strong style="color:#ffffff;">$1</strong>');
|
|
html = html.replace(/__(.*?)__/g, '<strong style="color:#ffffff;">$1</strong>');
|
|
html = html.replace(/\*(.*?)\*/g, '<em style="color:#cbd5e1;">$1</em>');
|
|
html = html.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer" style="color:#34d399;text-decoration:underline;">$1</a>');
|
|
html = html.replace(/^[-*+]\s+(.*?)$/gm, '<li style="margin-left:20px;list-style-type:disc;color:#cbd5e1;padding:2px 0;">$1</li>');
|
|
html = html.replace(/^\d+\.\s+(.*?)$/gm, '<li style="margin-left:20px;list-style-type:decimal;color:#cbd5e1;padding:2px 0;">$1</li>');
|
|
|
|
const paragraphs = html.split(/\n{2,}/);
|
|
html = paragraphs
|
|
.map((p) => {
|
|
const trimmed = p.trim();
|
|
if (!trimmed) return '';
|
|
if (
|
|
trimmed.startsWith('<h') ||
|
|
trimmed.startsWith('<div') ||
|
|
trimmed.startsWith('<table') ||
|
|
trimmed.startsWith('<pre') ||
|
|
trimmed.startsWith('<blockquote') ||
|
|
trimmed.startsWith('<hr') ||
|
|
trimmed.startsWith('<li')
|
|
) {
|
|
return trimmed;
|
|
}
|
|
return `<p style="margin:8px 0;line-height:1.6;color:#cbd5e1;">${trimmed.replace(/\n/g, '<br />')}</p>`;
|
|
})
|
|
.join('\n');
|
|
|
|
return html;
|
|
}
|
|
|