fix(ui): stop stamping a dead blur class on chat links containing 'tail' (#123154)

The DOMPurify afterSanitizeAttributes hook added class
chat-link-tail-blur to every rendered chat link whose href merely
contains the substring "tail" (docs.openclaw.ai/tailscale, /details/,
retail, ...). No stylesheet or script anywhere consumes the class: it is
an orphaned producer that survived the Control UI import, polluting
sanitized HTML and the markdown LRU cache, and priming a foot-gun where
any future .chat-link-tail-blur CSS rule would visually blur docs links
in every transcript.

Link presentation classes are owned by markdown-parser.ts
(web-link-classes, hostname-based); delete the substring classifier.
This commit is contained in:
Peter Steinberger
2026-08-13 05:22:02 -07:00
committed by GitHub
parent 5739c1951b
commit 0926d56cbf
2 changed files with 9 additions and 5 deletions
+9
View File
@@ -50,6 +50,15 @@ describe("toSanitizedMarkdownHtml", () => {
);
});
it("does not stamp presentation classes on links whose href contains 'tail'", () => {
const fragment = htmlFragment(
toSanitizedMarkdownHtml("[tailscale docs](https://docs.openclaw.ai/tailscale)"),
);
const link = fragment.querySelector("a");
expect(link?.getAttribute("href")).toBe("https://docs.openclaw.ai/tailscale");
expect(link?.classList.contains("chat-link-tail-blur")).toBe(false);
});
it("strips unsupported citation control markers before display", () => {
const html = toSanitizedMarkdownHtml(
"v2026.5.20 release note citeturn2view0\n\nStill readable.",
-5
View File
@@ -1,4 +1,3 @@
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
// Control UI module implements markdown behavior.
import DOMPurify from "dompurify";
import { stripUnsupportedCitationControlMarkers } from "../../../src/shared/text/citation-control-markers.js";
@@ -318,7 +317,6 @@ const APP_RESOURCE_PATH_PREFIXES = [
["plugins", "diffs-language-pack"],
];
const markdownCache = new Map<string, string>();
const TAIL_LINK_BLUR_CLASS = "chat-link-tail-blur";
function getCachedMarkdown(key: string): string | null {
const cached = markdownCache.get(key);
@@ -469,9 +467,6 @@ function installHooks() {
node.setAttribute("rel", "noreferrer noopener");
node.setAttribute("target", "_blank");
if (normalizeLowercaseStringOrEmpty(href).includes("tail")) {
node.classList.add(TAIL_LINK_BLUR_CLASS);
}
});
}