diff --git a/src/telegram/format.ts b/src/telegram/format.ts index a9ca7bdc6fa..2972acb9845 100644 --- a/src/telegram/format.ts +++ b/src/telegram/format.ts @@ -222,11 +222,17 @@ export function wrapFileReferencesInHtml(html: string): string { `([^a-zA-Z0-9]|^)([A-Za-z]\\.(?:${extensionsPattern}))(?=[^a-zA-Z0-9/]|$)`, "g", ); - result = result.replace(orphanedTldPattern, (m, prefix, tld) => { - // Skip if already wrapped in a tag (check for < before or > after in context) + result = result.replace(orphanedTldPattern, (m, prefix, tld, offset) => { + // Skip if prefix is > (right after a tag close) if (prefix === ">") { return m; } + // Skip if we're inside an HTML tag (between < and >) + const lastOpen = result.lastIndexOf("<", offset); + const lastClose = result.lastIndexOf(">", offset); + if (lastOpen > lastClose) { + return m; // Inside a tag + } return `${prefix}${escapeHtml(tld)}`; }); @@ -239,8 +245,8 @@ export function renderTelegramHtmlText( ): string { const textMode = options.textMode ?? "markdown"; if (textMode === "html") { - // For HTML mode, still wrap file references in the HTML - return wrapFileReferencesInHtml(text); + // For HTML mode, trust caller markup - don't modify + return text; } // markdownToTelegramHtml already wraps file references by default return markdownToTelegramHtml(text, { tableMode: options.tableMode }); diff --git a/src/telegram/format.wrap-md.test.ts b/src/telegram/format.wrap-md.test.ts index 5668b3d9661..413b91c1576 100644 --- a/src/telegram/format.wrap-md.test.ts +++ b/src/telegram/format.wrap-md.test.ts @@ -101,9 +101,11 @@ describe("renderTelegramHtmlText - file reference wrapping", () => { expect(result).toContain("README.md"); }); - it("wraps file references in HTML mode", () => { + it("does not wrap in HTML mode (trusts caller markup)", () => { + // textMode: "html" should pass through unchanged - caller owns the markup const result = renderTelegramHtmlText("Check README.md", { textMode: "html" }); - expect(result).toContain("README.md"); + expect(result).toBe("Check README.md"); + expect(result).not.toContain(""); }); it("does not double-wrap already code-formatted content", () => { @@ -324,4 +326,19 @@ describe("edge cases", () => { // Only x.md gets wrapped, the rest passes through expect(result).toBe("x.mdbold"); }); + + it("does not wrap orphaned TLD inside href attributes", () => { + // D.md inside href should NOT be wrapped + const input = 'link'; + const result = wrapFileReferencesInHtml(input); + // href should be untouched + expect(result).toBe(input); + expect(result).not.toContain("D.md"); + }); + + it("does not wrap orphaned TLD inside any HTML attribute", () => { + const input = 'R&D.md'; + const result = wrapFileReferencesInHtml(input); + expect(result).toBe(input); + }); });