Make any frontmatter <scheme>:// URI's clickable (#1822)

* Make any frontmatter <scheme>:// URI's clickable

http/https links are opened in a new tab/window (via target="_blank"),
other schemes are opened in the current tab, as the browser will prompt
the user to open another application (or do this automatically).

Per https://www.rfc-editor.org/rfc/rfc3986#section-3.1, the scheme must
follow the pattern:

    scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )

* Make `mailto:...` strings clickable in frontmatter
This commit is contained in:
rktjmp
2026-02-06 16:01:39 +01:00
committed by GitHub
parent dcf4f126e6
commit 2c1cc8ced1
2 changed files with 48 additions and 3 deletions
+46 -2
View File
@@ -12,6 +12,7 @@ import {
frontmatterQuotesRegex,
frontmatterUrlRegex,
frontmatterWikiLinkRegex,
frontmatterMailtoRegex,
} from "../markdown_parser/constants.ts";
import { processWikiLink, type WikiLinkMatch } from "./wiki_link_processor.ts";
@@ -78,7 +79,7 @@ export function frontmatterPlugin(client: Client) {
const to = from + oMatch[0].length;
const text = state.sliceDoc(from, to);
// 1) External links: http(s) URLs
// 1) External links: http(s), <scheme>:// URLs
frontmatterUrlRegex.lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = frontmatterUrlRegex.exec(text)) !== null) {
@@ -103,7 +104,14 @@ export function frontmatterPlugin(client: Client) {
return;
}
try {
globalThis.open(url, "_blank");
// Open http(s) links in a new window/tab, open
// alternate schemes in the same page, as they'll
// bounce to another application.
if (/^https?:\/\//i.test(url)) {
globalThis.open(url, "_blank");
} else {
globalThis.open(url, "_self");
}
} catch (err) {
console.error("Failed to open external link", err);
}
@@ -160,6 +168,42 @@ export function frontmatterPlugin(client: Client) {
widgets.push(...decorations);
}
// 3) mailto:... links
frontmatterMailtoRegex.lastIndex = 0;
let mMatch: RegExpExecArray | null;
while ((mMatch = frontmatterMailtoRegex.exec(text)) !== null) {
const mFrom = from + (mMatch.index ?? 0);
const mTo = mFrom + mMatch[0].length;
const url = mMatch[1];
const address = url.slice(7);
widgets.push(
Decoration.replace({
widget: new LinkWidget({
text: url,
title: `Mail ${address}`,
href: url,
cssClass: "sb-external-link",
from: mFrom,
callback: (e) => {
if (e.altKey) {
// Move cursor into the link
client.editorView.dispatch({
selection: { anchor: mFrom },
});
client.focus();
return;
}
try {
globalThis.open(url, "_self");
} catch (err) {
console.error("Failed to open external link", err);
}
},
}),
}).range(mFrom, mTo),
);
}
}
}
},
+2 -1
View File
@@ -6,7 +6,8 @@ export const tagRegex =
export const nakedUrlRegex =
/(^https?:\/\/([-a-zA-Z0-9@:%_\+~#=]|(?:[.](?!(\s|$)))){1,256})(([-a-zA-Z0-9(@:%_\+~#?&=\/]|(?:[.,:;)](?!(\s|$))))*)/;
export const frontmatterQuotesRegex = /["'].*["']/g;
export const frontmatterUrlRegex = /(https?:\/\/[^\s"']+)/g;
export const frontmatterUrlRegex = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s"']+)/g;
export const frontmatterWikiLinkRegex =
/(?<leadingTrivia>!?\[\[)(?<stringRef>.*?)(?:\|(?<alias>.*?))?(?<trailingTrivia>\]\])/g;
export const frontmatterMailtoRegex = /(mailto:[^@\s]+@[^@\s"']+)/ig;
export const pWikiLinkRegex = new RegExp("^" + wikiLinkRegex.source); // Modified regex used only in parser