






















@@ -1016,6 +1016,102 @@ function renderTelegramRichHtmlDocument(
10161016);
10171017}
101810181019+function convertTelegramRichSegmentNewlines(
1020+segment: string,
1021+prevStructural: boolean,
1022+nextStructural: boolean,
1023+): string {
1024+if (!segment.includes("\n")) {
1025+return segment;
1026+}
1027+// Keep newline runs that hug a structural tag: Telegram already starts a new
1028+// line there, so a stray <br> would add a blank line or land as an invalid
1029+// child inside a container (table/figure/details/list).
1030+return segment.replace(/\n+/g, (run: string, offset: number) => {
1031+const hugsPrev = offset === 0 && prevStructural;
1032+const hugsNext = offset + run.length === segment.length && nextStructural;
1033+return hugsPrev || hugsNext ? run : "<br>".repeat(run.length);
1034+});
1035+}
1036+1037+// Tags whose inner whitespace Telegram renders verbatim, so their newlines stay
1038+// literal: code/pre keep source formatting and math holds raw LaTeX.
1039+const TELEGRAM_RICH_LITERAL_WHITESPACE_TAGS = new Set(["code", "pre", "tg-math", "tg-math-block"]);
1040+1041+// Structural tags whose surrounding/inner newlines are layout whitespace, not
1042+// prose: the rich block set plus the table/figure/details container children
1043+// that TELEGRAM_RICH_BLOCK_HTML_TAGS omits (it is tuned for chunk block
1044+// counting). A <br> wedged between these would be an invalid container child or
1045+// a stray blank line, so their boundary newlines stay literal.
1046+const TELEGRAM_RICH_LINE_BREAK_STRUCTURAL_TAGS: ReadonlySet<string> = new Set([
1047+ ...TELEGRAM_RICH_BLOCK_HTML_TAGS,
1048+"caption",
1049+"col",
1050+"colgroup",
1051+"figcaption",
1052+"summary",
1053+"tbody",
1054+"td",
1055+"tfoot",
1056+"th",
1057+"thead",
1058+]);
1059+1060+function isTelegramRichLineBreakStructuralTag(rawTag: string, tagName: string): boolean {
1061+return (
1062+TELEGRAM_RICH_LINE_BREAK_STRUCTURAL_TAGS.has(tagName) ||
1063+(tagName === "a" && /\sname="[^"]+"/i.test(rawTag))
1064+);
1065+}
1066+1067+// Bot API 10.1 rich messages parse structured HTML, so literal newlines are
1068+// insignificant whitespace — unlike the legacy HTML parse mode that renders them
1069+// as line breaks. Materialize inline newlines as <br> so multi-line prose and
1070+// bullet runs keep their breaks, while leaving newlines literal inside
1071+// code/pre/math and where they only separate block-level tags.
1072+export function materializeTelegramRichHtmlLineBreaks(html: string): string {
1073+if (!html.includes("\n")) {
1074+return html;
1075+}
1076+let result = "";
1077+let lastIndex = 0;
1078+let literalDepth = 0;
1079+let prevStructural = false;
1080+1081+HTML_TAG_PATTERN.lastIndex = 0;
1082+let match: RegExpExecArray | null;
1083+while ((match = HTML_TAG_PATTERN.exec(html)) !== null) {
1084+const tagStart = match.index;
1085+const tagEnd = HTML_TAG_PATTERN.lastIndex;
1086+const rawTag = match[0];
1087+const isClosing = match[1] === "</";
1088+const tagName = normalizeLowercaseStringOrEmpty(match[2]);
1089+// <br> already emits a break, so treat it like a structural boundary: a
1090+// hugging newline stays literal instead of doubling into a blank line.
1091+const tagIsStructural =
1092+tagName === "br" || isTelegramRichLineBreakStructuralTag(rawTag, tagName);
1093+const segment = html.slice(lastIndex, tagStart);
1094+result +=
1095+literalDepth > 0
1096+ ? segment
1097+ : convertTelegramRichSegmentNewlines(segment, prevStructural, tagIsStructural);
1098+1099+// Self-closing literal tags (e.g. a stray <pre/>) must not open a region that
1100+// never closes and swallows every later line break.
1101+if (TELEGRAM_RICH_LITERAL_WHITESPACE_TAGS.has(tagName) && !rawTag.trimEnd().endsWith("/>")) {
1102+literalDepth = isClosing ? Math.max(0, literalDepth - 1) : literalDepth + 1;
1103+}
1104+result += rawTag;
1105+lastIndex = tagEnd;
1106+prevStructural = tagIsStructural;
1107+}
1108+1109+const tail = html.slice(lastIndex);
1110+result +=
1111+literalDepth > 0 ? tail : convertTelegramRichSegmentNewlines(tail, prevStructural, false);
1112+return result;
1113+}
1114+10191115export function markdownToTelegramRichHtml(
10201116markdown: string,
10211117options: { tableMode?: MarkdownTableMode; skipEntityDetection?: boolean } = {},
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。