fix(frontend): recognize tab-separated web fetch headings (#6923)

Co-authored-by: JY-M666 <316074816+JY-M666@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
JY-M666 2026-10-16 11:01:02 +08:00 committed by GitHub
parent 9c74b1529b
commit ec07de9618
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 25 additions and 3 deletions

View File

@ -117,8 +117,9 @@ Read [src/AGENTS.md](src/AGENTS.md) for component and domain ownership.
## Code Style
`core/utils/markdown.ts` reads web-fetch titles from the first nonblank line.
Match zero to three literal spaces before `# ` without trimming indentation;
mixed space/tab code blocks must fall back to the URL. Strip optional closing
Match zero to three literal spaces before a single `#` followed by an ASCII
space or tab, without trimming indentation; mixed space/tab code blocks must
fall back to the URL. Strip optional closing
hashes with a backwards scan so long fetched titles cannot cause quadratic regex
backtracking. Closing hashes require a preceding space/tab (or an empty title)
and only ASCII spaces/tabs afterward, apart from the terminal CR in CRLF input.

View File

@ -5,7 +5,7 @@ export function extractTitleFromMarkdown(markdown: string) {
if (firstLine === undefined) {
return undefined;
}
const headingPrefix = /^ {0,3}# /.exec(firstLine);
const headingPrefix = /^ {0,3}#[ \t]/.exec(firstLine);
if (!headingPrefix) {
return undefined;
}

View File

@ -6,6 +6,27 @@ test("reads the title from a leading ATX heading", () => {
expect(extractTitleFromMarkdown("# Real Title\n\nbody")).toBe("Real Title");
});
test.each([
["#\tReal Title", "Real Title"],
["#\t Real Title", "Real Title"],
["# \tReal Title", "Real Title"],
[" #\tReal Title\r\nbody", "Real Title"],
["#\tReal Title ###\t\r\nbody", "Real Title"],
["#\t", undefined],
["#\t###", undefined],
])("accepts ASCII tab separators in %j", (markdown, expected) => {
expect(extractTitleFromMarkdown(markdown)).toBe(expected);
});
test.each(["\u00a0", "\u2003", "\v", "\f"])(
"does not treat %j as an ATX separator",
(separator) => {
expect(
extractTitleFromMarkdown(`#${separator}Not A Title`),
).toBeUndefined();
},
);
test.each([
["# Release Notes ###", "Release Notes"],
["# Release Notes ### ", "Release Notes"],