我如何将 ChatGPT 放入编辑器中

随着所有炒作的进行,人工智能(或者更确切地说是机器学习(ML)和大型语言模型(LLM))无处不在。就个人而言,我可能不会经常使用 ChatGPT(和类似的替代品),但我确实依赖GitHub Copilot之类的东西(用于 VS Code 中的智能自动完成)或Grammarly(用于编辑我的博客文章)每天。

我认为我们距离 AGI 仍有相当多的突破,而目前的技术不足以让我们到达那里(谢天谢地或不谢天谢地)。也就是说,我们已经深入到“AI 增强型”应用程序的时代,顶级应用程序可能没有最好的 AI 系统,但它们以最好的方式集成了它们。

这就是为什么这是一个有趣的过程,探索 OpenAI 的 API 并尝试将其集成到 Vrite 的富文本编辑器 (RTE) — 我的开源无头 CMS。

扩展所见即所得编辑器

对于那些不熟悉的人来说,简而言之,Vrite 是一个用于技术内容的无头 CMS,例如编程博客或软件文档。它可以看作是两个应用程序合二为一——用于内容管理的看板仪表板和用于编写的所见即所得编辑器,以及嵌入式代码片段编辑器和格式化程序等附加的开发友好功能。

Vrite 的最新重要补充是早期扩展系统,可以轻松构建集成并扩展 Vrite 的功能。对我来说,这似乎是将 ChatGPT 作为扩展引入编辑器的完美方式

阻止操作

为了能够使用扩展系统将 ChatGPT 集成到编辑器中,必须引入一个新的 API。我称它为Block Action API,因为它专门用于向编辑器添加快速操作,对顶级内容块(如段落、标题或图像)进行操作,如下所示:

使用 Block Actions API,扩展可以读取活动块的 JSON 内容并使用 HTML 格式的内容更新它,就像在 Vrite API 中所做的一样(一方面,解析 JSON 输出更容易,另一方面,HTML 更容易)适合将内容转换成)。

从 UI 端来看,块操作显示为主动选择的块一侧的按钮。他们可以直接在点击时调用一个动作,或者——就像 ChatGPT 一样——打开一个下拉菜单来提示用户提供更多详细信息。

按钮必须绝对定位,这需要自定义TipTap扩展和更深入地挖掘底层ProseMirror(这两个库都支持 Vrite 编辑器)。

该过程基本上归结为确定块节点的位置和大小,给定整个顶级节点或仅其子节点的选择(源代码):

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">BlockActionMenuPlugin</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Extension</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">create</span><span style="color:var(--syntax-text-color)">({</span>
  <span style="color:var(--syntax-comment-color)">// ...</span>
  <span style="color:var(--syntax-name-color)">onSelectionUpdate</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">selection</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">state</span><span style="color:var(--syntax-text-color)">;</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">isTextSelection</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">selection</span> <span style="color:var(--syntax-declaration-color)">instanceof</span> <span style="color:var(--syntax-name-color)">TextSelection</span><span style="color:var(--syntax-text-color)">;</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">selectedNode</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">||</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeAfter</span><span style="color:var(--syntax-text-color)">;</span>

    <span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">selectedNode</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
      <span style="color:var(--syntax-name-color)">box</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">style</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">display</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">none</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
      <span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span>
    <span style="color:var(--syntax-text-color)">}</span>

    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">view</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">;</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">node</span> <span style="color:var(--syntax-error-color)">=</span>
      <span style="color:var(--syntax-name-color)">view</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeDOM</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">||</span>
      <span style="color:var(--syntax-name-color)">view</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeDOM</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">parentOffset</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">||</span>
      <span style="color:var(--syntax-name-color)">view</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">domAtPos</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span><span style="color:var(--syntax-text-color)">)?.</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">;</span>

    <span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span>

    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">blockParent</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">getBlockParent</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">);</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">parentPos</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">document</span>
      <span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">getElementById</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">pm-container</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">)</span>
      <span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">getBoundingClientRect</span><span style="color:var(--syntax-text-color)">();</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">childPos</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">blockParent</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">getBoundingClientRect</span><span style="color:var(--syntax-text-color)">();</span>

    <span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">parentPos</span> <span style="color:var(--syntax-error-color)">||</span> <span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span>

    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">relativePos</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">{</span>
      <span style="color:var(--syntax-name-color)">top</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">top</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">top</span><span style="color:var(--syntax-text-color)">,</span>
      <span style="color:var(--syntax-name-color)">right</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">right</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">right</span><span style="color:var(--syntax-text-color)">,</span>
      <span style="color:var(--syntax-name-color)">bottom</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">bottom</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">bottom</span><span style="color:var(--syntax-text-color)">,</span>
      <span style="color:var(--syntax-name-color)">left</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">left</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">left</span><span style="color:var(--syntax-text-color)">,</span>
    <span style="color:var(--syntax-text-color)">};</span>

    <span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-name-c
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值