diff --git a/src/telegram/format.ts b/src/telegram/format.ts
index 2972acb9845..501b6ac776f 100644
--- a/src/telegram/format.ts
+++ b/src/telegram/format.ts
@@ -222,14 +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, offset) => {
+ // Snapshot for offset calculations (offset is relative to pre-replacement string)
+ // Note: replace() doesn't mutate, but snapshot makes intent explicit
+ const snapshot = result;
+ result = snapshot.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);
+ const lastOpen = snapshot.lastIndexOf("<", offset);
+ const lastClose = snapshot.lastIndexOf(">", offset);
if (lastOpen > lastClose) {
return m; // Inside a tag
}
diff --git a/src/telegram/format.wrap-md.test.ts b/src/telegram/format.wrap-md.test.ts
index 413b91c1576..96b7628f71d 100644
--- a/src/telegram/format.wrap-md.test.ts
+++ b/src/telegram/format.wrap-md.test.ts
@@ -341,4 +341,22 @@ describe("edge cases", () => {
const result = wrapFileReferencesInHtml(input);
expect(result).toBe(input);
});
+
+ it("handles multiple orphaned TLDs with HTML tags (offset stability)", () => {
+ // This tests the bug where offset is relative to pre-replacement string
+ // but we were checking against the mutating result string
+ const input = 'link B.md text D.py';
+ const result = wrapFileReferencesInHtml(input);
+ // A.md in href should NOT be wrapped (inside attribute)
+ // B.md outside tags SHOULD be wrapped
+ // C.sh in title attribute should NOT be wrapped
+ // D.py outside tags SHOULD be wrapped
+ expect(result).toContain("B.md");
+ expect(result).toContain("D.py");
+ expect(result).not.toContain("A.md");
+ expect(result).not.toContain("C.sh");
+ // Attributes should be unchanged
+ expect(result).toContain('href="http://A.md"');
+ expect(result).toContain('title="C.sh"');
+ });
});