1
0

Fixes #6 by implementing link unfurling

This commit is contained in:
Zef Hemel 2022-07-30 13:39:40 +02:00
parent b5ba23130b
commit 3217f2afc3
4 changed files with 112 additions and 1 deletions

View File

@ -39,6 +39,7 @@ let vm = new VM({
clearTimeout,
setInterval,
URL,
window: {},
clearInterval,
fetch: require(`${nodeModulesPath}/node-fetch`),
WebSocket: require(`${nodeModulesPath}/ws`),

View File

@ -11,8 +11,16 @@ let pendingRequests = new Map<
}
>();
let isWorker = false;
if (typeof window === "undefined") {
// @ts-ignore
window = {};
isWorker = true;
}
function workerPostMessage(msg: ControllerMessage) {
if (typeof window !== "undefined" && window.parent !== window) {
if (!isWorker && window.parent !== window) {
window.parent.postMessage(msg, "*");
} else {
self.postMessage(msg);

View File

@ -319,3 +319,26 @@ functions:
name: "UI: Hide BHS"
key: "Ctrl-Alt-b"
mac: "Cmd-Alt-b"
# Link unfurl infrastructure
unfurlLink:
path: ./link.ts:unfurlCommand
command:
name: "Link: Unfurl"
key: "Ctrl-Shift-u"
mac: "Cmd-Shift-u"
contexts:
- NakedURL
unfurlExec:
env: server
path: ./link.ts:unfurlExec
# Title-based link unfurl
titleUnfurlOptions:
path: ./link.ts:titleUnfurlOptions
events:
- unfurl:options
titleUnfurl:
path: ./link.ts:titleUnfurl
events:
- unfurl:title-unfurl

View File

@ -0,0 +1,79 @@
import { nodeAtPos } from "@silverbulletmd/common/tree";
import {
filterBox,
flashNotification,
getCursor,
getText,
replaceRange,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import { dispatch as dispatchEvent } from "@plugos/plugos-syscall/event";
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
type UnfurlOption = {
id: string;
name: string;
};
export async function unfurlCommand() {
let mdTree = await parseMarkdown(await getText());
let nakedUrlNode = nodeAtPos(mdTree, await getCursor());
let url = nakedUrlNode!.children![0].text!;
console.log("Got URL to unfurl", url);
let optionResponses = await dispatchEvent("unfurl:options", url);
let options: UnfurlOption[] = [];
for (let resp of optionResponses) {
options.push(...resp);
}
let selectedUnfurl: any = await filterBox(
"Unfurl",
options,
"Select the unfurl strategy of your choice"
);
if (!selectedUnfurl) {
return;
}
try {
let replacement = await invokeFunction(
"server",
"unfurlExec",
selectedUnfurl.id,
url
);
await replaceRange(nakedUrlNode?.from!, nakedUrlNode?.to!, replacement);
} catch (e: any) {
await flashNotification(e.message, "error");
}
}
export async function titleUnfurlOptions(url: string): Promise<UnfurlOption[]> {
return [
{
id: "title-unfurl",
name: "Extract title",
},
];
}
// Run on the server because plugs will likely rely on fetch for this
export async function unfurlExec(id: string, url: string): Promise<string> {
let replacement = await dispatchEvent(`unfurl:${id}`, url);
return replacement[0];
}
const titleRegex = /<title[^>]*>\s*([^<]+)\s*<\/title\s*>/i;
export async function titleUnfurl(url: string): Promise<string> {
let response = await fetch(url);
if (response.status < 200 || response.status >= 300) {
console.error("Unfurl failed", await response.text());
throw new Error(`Failed to fetch: ${await response.statusText}`);
}
let body = await response.text();
let match = titleRegex.exec(body);
if (match) {
return `[${match[1]}](${url})`;
} else {
throw new Error("No title found");
}
}