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:
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user