import { countEmoji, FLOOD_DENSITY_THRESHOLD, FLOOD_COUNT_THRESHOLD } from './emojiFlood' const HTML_TAG_RE = /<[a-z][^>]*>/i const MAX_CONTENT_LENGTH = 10000 function decodeHtmlEntities(value) { const decoded = String(value || '') if (typeof document === 'undefined') { return decoded .replace(/ /gi, ' ') .replace(/&/gi, '&') .replace(/</gi, '<') .replace(/>/gi, '>') .replace(/"/gi, '"') .replace(/'/gi, "'") } const textarea = document.createElement('textarea') textarea.innerHTML = decoded return textarea.value } function stripResidualTags(value) { return String(value || '').replace(/<[^>]+>/g, '') } export function normalizeMarkdownLiteContent(value) { const raw = String(value || '') const trimmed = raw.trim() if (!trimmed || !HTML_TAG_RE.test(trimmed)) { return raw } const normalized = raw .replace(/<\s*a[^>]*href=(['"])(.*?)\1[^>]*>([\s\S]*?)<\s*\/a\s*>/gi, (_, __, href, label) => { const text = stripResidualTags(label).trim() || href return `[${text}](${href})` }) .replace(/<\s*(strong|b)(?:\s+[^>]*)?>([\s\S]*?)<\s*\/\s*\1\s*>/gi, (_, __, text) => `**${stripResidualTags(text)}**`) .replace(/<\s*(em|i)(?:\s+[^>]*)?>([\s\S]*?)<\s*\/\s*\1\s*>/gi, (_, __, text) => `*${stripResidualTags(text)}*`) .replace(/<\s*code(?:\s+[^>]*)?>([\s\S]*?)<\s*\/code\s*>/gi, (_, text) => `\`${stripResidualTags(text)}\``) .replace(/<\s*br\s*\/?>/gi, '\n') .replace(/<\s*\/p\s*>/gi, '\n\n') .replace(/<\s*p(?:\s+[^>]*)?>/gi, '') .replace(/<\s*li(?:\s+[^>]*)?>([\s\S]*?)<\s*\/li\s*>/gi, (_, text) => `- ${stripResidualTags(text).trim()}\n`) .replace(/<\s*\/ul\s*>|<\s*\/ol\s*>/gi, '\n') .replace(/<\s*(ul|ol)(?:\s+[^>]*)?>/gi, '') .replace(/<\s*blockquote(?:\s+[^>]*)?>([\s\S]*?)<\s*\/blockquote\s*>/gi, (_, text) => { const lines = stripResidualTags(text) .split(/\r?\n/) .map((line) => line.trim()) .filter(Boolean) .map((line) => `> ${line}`) return `${lines.join('\n')}\n\n` }) .replace(/<[^>]+>/g, '') return decodeHtmlEntities(normalized) .replace(/\r\n?/g, '\n') .replace(/[\t ]+\n/g, '\n') .replace(/\n{3,}/g, '\n\n') .trim() } export function validateMarkdownLiteContent(value) { const raw = String(value || '') const trimmed = raw.trim() if (!trimmed) return [] const errors = [] if (trimmed.length > MAX_CONTENT_LENGTH) { errors.push('Content exceeds maximum length of 10,000 characters.') } if (HTML_TAG_RE.test(trimmed)) { errors.push('HTML tags are not allowed. Use Markdown formatting instead.') } const emojiCount = countEmoji(trimmed) if (emojiCount > FLOOD_COUNT_THRESHOLD) { errors.push('Too many emoji. Please limit emoji usage.') } if (emojiCount > 5 && trimmed.length > 0 && (emojiCount / trimmed.length) > FLOOD_DENSITY_THRESHOLD) { errors.push('Content is mostly emoji. Please add some text.') } return errors }