diff --git a/src/assets/styles/global.css b/src/assets/styles/global.css
index b03009a..ab73d35 100644
--- a/src/assets/styles/global.css
+++ b/src/assets/styles/global.css
@@ -1437,6 +1437,84 @@ body {
border-color: rgba(160, 160, 170, 0.32);
}
+/* ── GitHub issue/PR — Green ────────────────────────── */
+
+.prose .ref-gh-issue {
+ color: #1a6d2e;
+ background-color: rgba(34, 140, 60, 0.08);
+ border-color: rgba(34, 140, 60, 0.2);
+}
+
+.prose .ref-gh-issue:hover {
+ color: #145524;
+ background-color: rgba(34, 140, 60, 0.14);
+ border-color: rgba(34, 140, 60, 0.35);
+}
+
+.dark .prose .ref-gh-issue {
+ color: #6ddb8a;
+ background-color: rgba(50, 180, 80, 0.12);
+ border-color: rgba(50, 180, 80, 0.25);
+}
+
+.dark .prose .ref-gh-issue:hover {
+ color: #8de8a4;
+ background-color: rgba(50, 180, 80, 0.2);
+ border-color: rgba(50, 180, 80, 0.4);
+}
+
+/* ── CVE — Red/rose ────────────────────────────────── */
+
+.prose .ref-cve {
+ color: #9f1239;
+ background-color: rgba(190, 30, 70, 0.07);
+ border-color: rgba(190, 30, 70, 0.18);
+}
+
+.prose .ref-cve:hover {
+ color: #7f0f2e;
+ background-color: rgba(190, 30, 70, 0.13);
+ border-color: rgba(190, 30, 70, 0.32);
+}
+
+.dark .prose .ref-cve {
+ color: #f0758e;
+ background-color: rgba(220, 60, 90, 0.12);
+ border-color: rgba(220, 60, 90, 0.25);
+}
+
+.dark .prose .ref-cve:hover {
+ color: #f5a0b2;
+ background-color: rgba(220, 60, 90, 0.2);
+ border-color: rgba(220, 60, 90, 0.4);
+}
+
+/* ── Python release — Python blue (distinct) ────────── */
+
+.prose .ref-py-release {
+ color: #1a5276;
+ background-color: rgba(30, 100, 160, 0.08);
+ border-color: rgba(30, 100, 160, 0.2);
+}
+
+.prose .ref-py-release:hover {
+ color: #144060;
+ background-color: rgba(30, 100, 160, 0.14);
+ border-color: rgba(30, 100, 160, 0.35);
+}
+
+.dark .prose .ref-py-release {
+ color: #68b5e8;
+ background-color: rgba(50, 130, 200, 0.12);
+ border-color: rgba(50, 130, 200, 0.25);
+}
+
+.dark .prose .ref-py-release:hover {
+ color: #90caf0;
+ background-color: rgba(50, 130, 200, 0.2);
+ border-color: rgba(50, 130, 200, 0.4);
+}
+
/* ── Post footer references panel ── */
.post-refs-header {
diff --git a/src/components/references/_icons.ts b/src/components/references/_icons.ts
index d78fedb..c8c5a20 100644
--- a/src/components/references/_icons.ts
+++ b/src/components/references/_icons.ts
@@ -33,6 +33,27 @@ export const docsIcon = lucide(
'',
);
+/** Lucide `circle-dot` — GitHub issue/PR */
+export const issueIcon = lucide(
+ 14,
+ 14,
+ '',
+);
+
+/** Lucide `shield-alert` — CVE / security vulnerability */
+export const shieldIcon = lucide(
+ 14,
+ 14,
+ '',
+);
+
+/** Lucide `download` — Python release download */
+export const downloadIcon = lucide(
+ 14,
+ 14,
+ '',
+);
+
/** Lucide `arrow-up-right` — external link indicator */
export const externalIcon = lucide(
11,
diff --git a/src/plugins/remark-python-refs.ts b/src/plugins/remark-python-refs.ts
index d3fecd0..094dbdb 100644
--- a/src/plugins/remark-python-refs.ts
+++ b/src/plugins/remark-python-refs.ts
@@ -8,8 +8,11 @@
* - PEP links (by URL pattern OR link text matching "PEP NNN")
* - CPython docs (docs.python.org/...)
* - PyPI links (pypi.org/project/NAME/)
+ * - GitHub issues/PRs (github.com/OWNER/REPO/issues|pull/NNN)
* - GitHub repos (github.com/OWNER/REPO — exactly 2 path segments)
* - GitHub users/orgs (github.com/NAME — exactly 1 segment, not reserved)
+ * - CVE references (nvd.nist.gov/vuln/detail/CVE-YYYY-NNNNN)
+ * - Python releases (python.org/downloads/release/python-XXXX/)
*/
import type { Root, Link, Paragraph, PhrasingContent } from "mdast";
import { visit } from "unist-util-visit";
@@ -18,6 +21,9 @@ import {
docsIcon,
githubIcon,
packageIcon,
+ issueIcon,
+ shieldIcon,
+ downloadIcon,
externalIcon,
} from "../components/references/_icons.js";
@@ -27,6 +33,9 @@ const PEP_OLD = /^https?:\/\/(?:www\.)?python\.org\/dev\/peps\/pep-(\d+)\/?/i;
const PEP_NEW = /^https?:\/\/peps\.python\.org\/pep-(\d+)\/?/i;
const DOCS = /^https?:\/\/docs\.python\.org\//i;
const PYPI = /^https?:\/\/pypi\.org\/project\/([^/]+)\/?/i;
+const GH_ISSUE = /^https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/(issues|pull)\/(\d+)\/?/i;
+const CVE = /^https?:\/\/nvd\.nist\.gov\/vuln\/detail\/(CVE-[\d-]+)\/?/i;
+const PY_RELEASE = /^https?:\/\/(?:www\.)?python\.org\/downloads\/release\/(python-[\w.]+)\/?/i;
const GITHUB =
/^https?:\/\/github\.com\/([\w.-]+)(?:\/([\w.-]+))?\/?$/i;
@@ -64,7 +73,7 @@ const GH_RESERVED = new Set([
// ── Helpers ─────────────────────────────────────────────
-type RefType = "pep" | "docs" | "pypi" | "gh-repo" | "gh-user";
+type RefType = "pep" | "docs" | "pypi" | "gh-repo" | "gh-user" | "gh-issue" | "cve" | "py-release";
interface Match {
type: RefType;
@@ -108,6 +117,18 @@ function classify(url: string, linkText?: string): Omit
return { type: "pypi", icon: packageIcon };
}
+ if ((m = url.match(GH_ISSUE))) {
+ return { type: "gh-issue", icon: issueIcon };
+ }
+
+ if ((m = url.match(CVE))) {
+ return { type: "cve", icon: shieldIcon };
+ }
+
+ if ((m = url.match(PY_RELEASE))) {
+ return { type: "py-release", icon: downloadIcon };
+ }
+
if ((m = url.match(GITHUB))) {
const [, owner, repo] = m;
if (repo) {