Growth the hard way

封装将 Firefox 中链接插入 Org-mode 的过程成为可分享的包

2023.01.15

TL:DR;

为了将前面的逻辑方便的分享出去,我基于 deno-bridge 封装了 deno-bridge-echo

将 Firefox 中的链接插入 Org-mode 是一个有用的方案,然而其实现涉及多种语言和工具,因此很难将其共享给其他人。为了解决这个问题,我使用 deno-bridge 工具封装了一个名为 deno-bridge-echo 的包,使其更易于使用和分享。现在,任何人都可以使用 deno-bridge-echo 来获取并插入来自 Firefox 的链接,而不必担心其底层实现细节。

问题

如何将之前的方案分享出去?

之前有群友建议说可以提 PR,但是由于这个方案混合使用了多个语言、工具,可能不大好去提到别的仓库中。

不过我想着这个方案自己用着没啥问题,而且实现过程非常简单,如果能将其分享出去也许能帮助到其他人呢。

是真正的问题吗?

是真正的问题。因为这个方案的实现本身很简单,但是不分享出去让别人知道,获得反馈的话,可能会影响到后续 创作的激情。而且用标准化的流程分享出去也是一种提高和积累。

探索解决办法

方案

按照之前的方案,需要三个端配合:浏览器侧的油猴脚本、Emacs 侧的 elisp,Python 侧的 HTTP 服务。

流程如下:

第一版实现方案的整体流程图

第一版实现方案的整体流程图

在旧的方案下需要手动去启动 Python 服务,我们当然可以写一个自启动脚本,但是我们使用 deno-bridge 来实现 的话会更容易。

该方案有以下优点:

  • deno-bridge 内部封装了通讯逻辑。 deno 服务跟 emacs-lisp 之间使用 websocket 通讯
  • deno 本身的优点:默认支持 TypeScript、不需要担心依赖问题、不需要操心该用哪个版本的 deno、用什么管理 依赖
  • 使 deno 可以做到单文件,这在分享软件的时候是非常方便的

现在我们的方案由以下几个部分组成:

浏览器侧的油猴脚本、Emacs 侧的 elisp、deno。再通过 deno-bridge 粘合起来。

代码

核心代码

目录结构:

./
├── README.org
├── deno-bridge-echo.el
├── deno-bridge-echo.ts
├── images
│   └── Tab-sync-flow-chart.svg
└── script.user.js

deno-bridge-echo.el

没有什么特殊的

deno-bridge-echo.ts

初始化 deno-bridge,用于处理 bridge 发送的信息

const bridge = new DenoBridge(  Deno.args[0],  Deno.args[1],  Deno.args[2],  messageDispatcher);

// 存放数据用的
const data: {
  currentTab: {
    url?: string;
    title?: string;
  };
} = { currentTab: {} };

async function messageDispatcher(message: string) {
  const [funcName, funcArgs] = JSON.parse(message)[1];
  if (funcName === "getCurrentTab") {
    const { currentTab } = data;
    if (!currentTab.url) {
      bridge.messageToEmacs("No Valid CurrentTab");
      return;
    }
    bridge.evalInEmacs(
      `(insert "[[${data.currentTab.url}][ ${currentTab.title}]]")`
    );
  }
}

其中,这块代码则表明如果接受到的操作是 getCurrentTab ,则在 Emacs 侧执行 elisp 代码

if (funcName === "getCurrentTab") {
    const { currentTab } = data;
    if (!currentTab.url) {
      bridge.messageToEmacs("No Valid CurrentTab");
      return;
    }
    bridge.evalInEmacs(
      `(insert "[[${data.currentTab.url}][ ${currentTab.title}]]")`
    );
  }

HTTP 服务

引入标准库 serve,监听在 8000 端口,并提供 /v1/echo 的 HTTP 服务

import { serve } from "https://deno.land/std/http/server.ts";

async function handler(req: Request): Promise<Response> {
  const url = new URL(req.url);
  if (url.pathname === "/v1/echo") {
    const payload = await req.json();
    data.currentTab = {
      url: payload.url,
      title: payload.title,
    };
    return new Response(JSON.stringify({ message: "already memorize" }), {
      status: 200,
      headers: {
        "content-type": "application/json; charset=utf-8",
      },
    });
  }
  const body = JSON.stringify({ message: "NOT FOUND" });

  return new Response(body, {
    status: 404,
    headers: {
      "content-type": "application/json; charset=utf-8",
    },
  });
}
serve(handler, { port: 8000 });

script.user.js 没有什么变动,可以直接在 Github 上看代码,deno-bridge-echo/deno-bridge-echo.el

分发

使用

参照 README 中的教程操作即可

https://github.com/nailuoGG/deno-bridge-echo/blob/main/README.org#install

总结

本次尝试将一份仅给自己用的脚本封装成 package,整个编写、发布流程比较顺畅。

其中使用了 deno-bridge,让使用更多语言来扩展 Emacs 能力成为了可能。

希望以后能创造更多的小工具