From 3217f2afc3fb15f54580525e2defd4fa0a7be7fa Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Sat, 30 Jul 2022 13:39:40 +0200 Subject: [PATCH] Fixes #6 by implementing link unfurling --- packages/plugos/environments/node_worker.ts | 1 + .../plugos/environments/sandbox_worker.ts | 10 ++- packages/plugs/core/core.plug.yaml | 23 ++++++ packages/plugs/core/link.ts | 79 +++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/plugs/core/link.ts diff --git a/packages/plugos/environments/node_worker.ts b/packages/plugos/environments/node_worker.ts index 1de9666..0fe9f96 100644 --- a/packages/plugos/environments/node_worker.ts +++ b/packages/plugos/environments/node_worker.ts @@ -39,6 +39,7 @@ let vm = new VM({ clearTimeout, setInterval, URL, + window: {}, clearInterval, fetch: require(`${nodeModulesPath}/node-fetch`), WebSocket: require(`${nodeModulesPath}/ws`), diff --git a/packages/plugos/environments/sandbox_worker.ts b/packages/plugos/environments/sandbox_worker.ts index a8961c8..90a991d 100644 --- a/packages/plugos/environments/sandbox_worker.ts +++ b/packages/plugos/environments/sandbox_worker.ts @@ -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); diff --git a/packages/plugs/core/core.plug.yaml b/packages/plugs/core/core.plug.yaml index cd5e147..0862bde 100644 --- a/packages/plugs/core/core.plug.yaml +++ b/packages/plugs/core/core.plug.yaml @@ -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 diff --git a/packages/plugs/core/link.ts b/packages/plugs/core/link.ts new file mode 100644 index 0000000..727c6b9 --- /dev/null +++ b/packages/plugs/core/link.ts @@ -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 { + 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 { + let replacement = await dispatchEvent(`unfurl:${id}`, url); + return replacement[0]; +} + +const titleRegex = /]*>\s*([^<]+)\s*<\/title\s*>/i; + +export async function titleUnfurl(url: string): Promise { + 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"); + } +}