make usernames linkable in news (#3200)

## Description:

make usernames linkable in news

now:
<img width="399" height="153" alt="image"
src="https://github.com/user-attachments/assets/39644fe2-9af1-4765-b839-9f8b5f9d0418"
/>


before:
<img width="409" height="82" alt="image"
src="https://github.com/user-attachments/assets/d7a1c37e-63cf-4417-ac61-c6db39a33851"
/>



## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

w.o.n

Co-authored-by: iamlewis <lewismmmm@gmail.com>
This commit is contained in:
Ryan
2026-02-16 19:11:10 +00:00
committed by GitHub
parent f362e47413
commit 4bc168dffb
3 changed files with 83 additions and 17 deletions
+30
View File
@@ -0,0 +1,30 @@
const GITHUB_PR_URL_REGEX =
/(?<!\()\bhttps:\/\/github\.com\/openfrontio\/OpenFrontIO\/pull\/(\d+)\b/g;
const GITHUB_COMPARE_URL_REGEX =
/(?<!\()\bhttps:\/\/github\.com\/openfrontio\/OpenFrontIO\/compare\/([\w.-]+)\b/g;
const GITHUB_MENTION_REGEX =
/(^|[^\w/[`])@([a-z\d](?:[a-z\d-]{0,37}[a-z\d])?)(?![\w-])/gim;
export function normalizeNewsMarkdown(markdown: string): string {
return (
markdown
// Convert bold header lines (e.g. "**Title**") into real Markdown headers.
// Exclude lines starting with - or * to avoid converting bullet points.
.replace(/^([^\-*\s].*?) \*\*(.+?)\*\*$/gm, "## $1 $2")
.replace(
GITHUB_PR_URL_REGEX,
(_match, prNumber) =>
`[#${prNumber}](https://github.com/openfrontio/OpenFrontIO/pull/${prNumber})`,
)
.replace(
GITHUB_COMPARE_URL_REGEX,
(_match, comparison) =>
`[${comparison}](https://github.com/openfrontio/OpenFrontIO/compare/${comparison})`,
)
.replace(
GITHUB_MENTION_REGEX,
(_match, prefix, username) =>
`${prefix}[@${username}](https://github.com/${username})`,
)
);
}
+4 -17
View File
@@ -6,6 +6,7 @@ import { translateText } from "../client/Utils";
import "./components/baseComponents/Modal";
import { BaseModal } from "./components/BaseModal";
import { modalHeader } from "./components/ui/ModalHeader";
import { normalizeNewsMarkdown } from "./NewsMarkdown";
import changelog from "/changelog.md?url";
import megaphone from "/images/Megaphone.svg?url";
@@ -67,23 +68,9 @@ export class NewsModal extends BaseModal {
this.initialized = true;
fetch(`${changelog}?v=${encodeURIComponent(version.trim())}`)
.then((response) => (response.ok ? response.text() : "Failed to load"))
.then((markdown) =>
markdown
// Convert bold header lines (e.g. "**Title**") into real Markdown headers
// Exclude lines starting with - or * to avoid converting bullet points
.replace(/^([^\-*\s].*?) \*\*(.+?)\*\*$/gm, "## $1 $2")
.replace(
/(?<!\()\bhttps:\/\/github\.com\/openfrontio\/OpenFrontIO\/pull\/(\d+)\b/g,
(_match, prNumber) =>
`[#${prNumber}](https://github.com/openfrontio/OpenFrontIO/pull/${prNumber})`,
)
.replace(
/(?<!\()\bhttps:\/\/github\.com\/openfrontio\/OpenFrontIO\/compare\/([\w.-]+)\b/g,
(_match, comparison) =>
`[${comparison}](https://github.com/openfrontio/OpenFrontIO/compare/${comparison})`,
),
)
.then((markdown) => (this.markdown = markdown));
.then((markdown) => normalizeNewsMarkdown(markdown))
.then((markdown) => (this.markdown = markdown))
.catch(() => (this.markdown = "Failed to load"));
}
}
}
+49
View File
@@ -0,0 +1,49 @@
import { normalizeNewsMarkdown } from "../../src/client/NewsMarkdown";
describe("normalizeNewsMarkdown", () => {
it("converts openfront pull request URLs to short markdown links", () => {
const input =
"Fix attack logic in https://github.com/openfrontio/OpenFrontIO/pull/1234";
const result = normalizeNewsMarkdown(input);
expect(result).toContain(
"[#1234](https://github.com/openfrontio/OpenFrontIO/pull/1234)",
);
});
it("converts openfront compare URLs to markdown links", () => {
const input =
"Full Changelog: https://github.com/openfrontio/OpenFrontIO/compare/v1.0.0...v1.1.0";
const result = normalizeNewsMarkdown(input);
expect(result).toContain(
"[v1.0.0...v1.1.0](https://github.com/openfrontio/OpenFrontIO/compare/v1.0.0...v1.1.0)",
);
});
it("converts github @mentions to profile links", () => {
const input = "- Feature by @evanpelle in release notes";
const result = normalizeNewsMarkdown(input);
expect(result).toContain("[@evanpelle](https://github.com/evanpelle)");
});
it("does not convert existing markdown-linked mentions", () => {
const input = "Credit [@evanpelle](https://github.com/evanpelle)";
const result = normalizeNewsMarkdown(input);
expect(result).toBe(input);
});
it("does not convert email addresses", () => {
const input = "Contact support@openfront.io for help";
const result = normalizeNewsMarkdown(input);
expect(result).toBe(input);
});
});