/// Утилиты для обработки markdown текста class MarkdownUtils { /// Обрабатывает markdown текст: /// - Исправляет неправильный синтаксис markdown ссылок /// - Автоматически преобразует URL в markdown ссылки /// - Преобразует **текст** в текст для надежного отображения жирного текста static String processMarkdown(String text) { // Сначала исправляем неправильный синтаксис markdown ссылок // Например: [http://example.com](url) -> [http://example.com](http://example.com) final wrongLinkRegex = RegExp(r'\[(https?://[^\]]+|www\.[^\]]+)\]\(([^\)]+)\)'); text = text.replaceAllMapped(wrongLinkRegex, (match) { final urlInText = match.group(1)!; if (!urlInText.startsWith('http://') && !urlInText.startsWith('https://') && !urlInText.startsWith('www.')) { return match.group(0)!; } final fullUrl = urlInText.startsWith('www.') ? 'https://$urlInText' : urlInText; return '[$urlInText]($fullUrl)'; }); // Находим все существующие markdown ссылки и их позиции final linkRegex = RegExp(r'\[([^\]]+)\]\(([^\)]+)\)'); final existingLinks = <_LinkRange>[]; for (final linkMatch in linkRegex.allMatches(text)) { existingLinks.add(_LinkRange(linkMatch.start, linkMatch.end)); } // Регулярное выражение для поиска URL (http://, https://, www.) final urlRegex = RegExp(r'(https?://[^\s\)]+|www\.[^\s\)]+)', caseSensitive: false); // Заменяем URL на markdown ссылки, если они еще не являются ссылками String result = text; int offset = 0; for (final urlMatch in urlRegex.allMatches(text)) { // Проверяем, не находится ли URL внутри существующей markdown ссылки bool isInsideLink = existingLinks.any((link) => urlMatch.start >= link.start && urlMatch.end <= link.end); if (!isInsideLink) { final url = urlMatch.group(0)!; final fullUrl = url.startsWith('www.') ? 'https://$url' : url; final replacement = '[$url]($fullUrl)'; result = result.replaceRange( urlMatch.start + offset, urlMatch.end + offset, replacement, ); offset += replacement.length - url.length; } } // Гарантируем, что жирный текст будет распознан // Заменяем **текст** на текст вручную для надежности final boldRegex = RegExp(r'\*\*([^*]+)\*\*'); result = result.replaceAllMapped(boldRegex, (match) { return '${match.group(1)}'; }); return result; } } /// Вспомогательный класс для хранения диапазона позиций ссылки class _LinkRange { final int start; final int end; _LinkRange(this.start, this.end); }