mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
fix: correct telegram html nesting (#4578) (thanks @ThanhNguyxn)
This commit is contained in:
@@ -74,6 +74,7 @@ Status: stable.
|
||||
|
||||
### Fixes
|
||||
- Telegram: use undici fetch for per-account proxy dispatcher. (#4456) Thanks @spiceoogway.
|
||||
- Telegram: fix HTML nesting for overlapping styles and links. (#4578) Thanks @ThanhNguyxn.
|
||||
- Telegram: avoid silent empty replies by tracking normalization skips before fallback. (#3796)
|
||||
- Telegram: accept numeric messageId/chatId in react action and honor channelId fallback. (#4533) Thanks @Ayush10.
|
||||
- Telegram: scope native skill commands to bound agent per bot. (#4360) Thanks @robhparker.
|
||||
|
||||
@@ -69,7 +69,6 @@ export function renderMarkdownWithMarkers(ir: MarkdownIR, options: RenderOptions
|
||||
}
|
||||
|
||||
const linkStarts = new Map<number, RenderLink[]>();
|
||||
const linkEnds = new Map<number, RenderLink[]>();
|
||||
if (options.buildLink) {
|
||||
for (const link of ir.links) {
|
||||
if (link.start === link.end) continue;
|
||||
@@ -80,15 +79,22 @@ export function renderMarkdownWithMarkers(ir: MarkdownIR, options: RenderOptions
|
||||
const openBucket = linkStarts.get(rendered.start);
|
||||
if (openBucket) openBucket.push(rendered);
|
||||
else linkStarts.set(rendered.start, [rendered]);
|
||||
const closeBucket = linkEnds.get(rendered.end);
|
||||
if (closeBucket) closeBucket.push(rendered);
|
||||
else linkEnds.set(rendered.end, [rendered]);
|
||||
}
|
||||
}
|
||||
|
||||
const points = [...boundaries].sort((a, b) => a - b);
|
||||
// Unified stack for both styles and links, tracking close string and end position
|
||||
const stack: { close: string; end: number }[] = [];
|
||||
type OpeningItem =
|
||||
| { end: number; open: string; close: string; kind: "link"; index: number }
|
||||
| {
|
||||
end: number;
|
||||
open: string;
|
||||
close: string;
|
||||
kind: "style";
|
||||
style: MarkdownStyle;
|
||||
index: number;
|
||||
};
|
||||
let out = "";
|
||||
|
||||
for (let i = 0; i < points.length; i += 1) {
|
||||
@@ -100,23 +106,51 @@ export function renderMarkdownWithMarkers(ir: MarkdownIR, options: RenderOptions
|
||||
if (item) out += item.close;
|
||||
}
|
||||
|
||||
// Open links first (so they close after styles that start at the same position)
|
||||
const openingItems: OpeningItem[] = [];
|
||||
|
||||
const openingLinks = linkStarts.get(pos);
|
||||
if (openingLinks && openingLinks.length > 0) {
|
||||
for (const link of openingLinks) {
|
||||
out += link.open;
|
||||
stack.push({ close: link.close, end: link.end });
|
||||
for (const [index, link] of openingLinks.entries()) {
|
||||
openingItems.push({
|
||||
end: link.end,
|
||||
open: link.open,
|
||||
close: link.close,
|
||||
kind: "link",
|
||||
index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Open styles second (so they close before links that start at the same position)
|
||||
const openingStyles = startsAt.get(pos);
|
||||
if (openingStyles) {
|
||||
for (const span of openingStyles) {
|
||||
for (const [index, span] of openingStyles.entries()) {
|
||||
const marker = styleMarkers[span.style];
|
||||
if (!marker) continue;
|
||||
out += marker.open;
|
||||
stack.push({ close: marker.close, end: span.end });
|
||||
openingItems.push({
|
||||
end: span.end,
|
||||
open: marker.open,
|
||||
close: marker.close,
|
||||
kind: "style",
|
||||
style: span.style,
|
||||
index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (openingItems.length > 0) {
|
||||
openingItems.sort((a, b) => {
|
||||
if (a.end !== b.end) return b.end - a.end;
|
||||
if (a.kind !== b.kind) return a.kind === "link" ? -1 : 1;
|
||||
if (a.kind === "style" && b.kind === "style") {
|
||||
return (STYLE_RANK.get(a.style) ?? 0) - (STYLE_RANK.get(b.style) ?? 0);
|
||||
}
|
||||
return a.index - b.index;
|
||||
});
|
||||
|
||||
// Open outer spans first (larger end) so LIFO closes stay valid for same-start overlaps.
|
||||
for (const item of openingItems) {
|
||||
out += item.open;
|
||||
stack.push({ close: item.close, end: item.end });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,4 +59,14 @@ describe("markdownToTelegramHtml", () => {
|
||||
const res = markdownToTelegramHtml("**bold [link](https://example.com) text**");
|
||||
expect(res).toBe('<b>bold <a href="https://example.com">link</a> text</b>');
|
||||
});
|
||||
|
||||
it("properly nests bold wrapping a link with trailing text", () => {
|
||||
const res = markdownToTelegramHtml("**[link](https://example.com) rest**");
|
||||
expect(res).toBe('<b><a href="https://example.com">link</a> rest</b>');
|
||||
});
|
||||
|
||||
it("properly nests bold inside a link", () => {
|
||||
const res = markdownToTelegramHtml("[**bold**](https://example.com)");
|
||||
expect(res).toBe('<a href="https://example.com"><b>bold</b></a>');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user