fix: preserve Mattermost mention indentation

This commit is contained in:
Muhammed Mukhthar CM
2026-03-10 17:24:41 +05:30
committed by GitHub
parent 64f015ab50
commit d30fff1776
3 changed files with 37 additions and 12 deletions

View File

@@ -58,6 +58,7 @@ Docs: https://docs.openclaw.ai
- Sandbox/subagents: pass the real configured workspace through `sessions_spawn` inheritance when a parent agent runs in a copied-workspace sandbox, so child `/agent` mounts point at the configured workspace instead of the parent sandbox copy. (#40757) Thanks @dsantoreis.
- Mattermost/plugin send actions: normalize direct `replyTo` fallback handling so threaded plugin sends trim blank IDs and reuse the correct reply target again. (#41176) Thanks @hnykda.
- MS Teams/allowlist resolution: use the General channel conversation ID as the resolved team key (with Graph GUID fallback) so Bot Framework runtime `channelData.team.id` matching works for team and team/channel allowlist entries. (#41838) Thanks @BradGroux.
- Mattermost/Markdown formatting: preserve first-line indentation when stripping bot mentions so nested list items and indented code blocks keep their structure, and render Mattermost tables natively by default instead of fenced-code fallback. (#18655) thanks @echo931.
## 2026.3.8

View File

@@ -61,10 +61,22 @@ describe("normalizeMention", () => {
expect(result).toContain(" - deep");
});
it("preserves first-line indentation for nested list items", () => {
const input = "@echobot\n - nested\n - deep";
const result = normalizeMention(input, "echobot");
expect(result).toBe(" - nested\n - deep");
});
it("preserves indented code blocks", () => {
const input = "@echobot\ntext\n code line 1\n code line 2";
const result = normalizeMention(input, "echobot");
expect(result).toContain(" code line 1");
expect(result).toContain(" code line 2");
});
it("preserves first-line indentation for indented code blocks", () => {
const input = "@echobot\n code line 1\n code line 2";
const result = normalizeMention(input, "echobot");
expect(result).toBe(" code line 1\n code line 2");
});
});

View File

@@ -80,16 +80,28 @@ export function normalizeMention(text: string, mention: string | undefined): str
return text.trim();
}
const escaped = mention.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const re = new RegExp(`@${escaped}\\b`, "gi");
// Replace the mention itself, then clean up without destroying Markdown structure.
// 1. Remove the mention (replace with empty to avoid injecting spaces into indentation)
// 2. Collapse only runs of multiple spaces/tabs within a line (preserving leading indent)
// 3. Trim blank lines left by mention removal
return text
.replace(re, "")
.split("\n")
.map((line) => line.replace(/(\S) {2,}/g, "$1 "))
.join("\n")
.replace(/^\s*\n/, "")
.trim();
const hasMentionRe = new RegExp(`@${escaped}\\b`, "i");
const leadingMentionRe = new RegExp(`^([\\t ]*)@${escaped}\\b[\\t ]*`, "i");
const trailingMentionRe = new RegExp(`[\\t ]*@${escaped}\\b[\\t ]*$`, "i");
const normalizedLines = text.split("\n").map((line) => {
const hadMention = hasMentionRe.test(line);
const normalizedLine = line
.replace(leadingMentionRe, "$1")
.replace(trailingMentionRe, "")
.replace(new RegExp(`@${escaped}\\b`, "gi"), "")
.replace(/(\S)[ \t]{2,}/g, "$1 ");
return {
text: normalizedLine,
mentionOnlyBlank: hadMention && normalizedLine.trim() === "",
};
});
while (normalizedLines[0]?.mentionOnlyBlank) {
normalizedLines.shift();
}
while (normalizedLines.at(-1)?.text.trim() === "") {
normalizedLines.pop();
}
return normalizedLines.map((line) => line.text).join("\n");
}