📝 Sign Up | 🔐 Log In

← Root | ↑ Up

┌──────────────────────────────────────────────────────────┐ │ 📄 shadcn/directory/udecode/plate/(plugins)/(ai)/ai.cn │ └──────────────────────────────────────────────────────────┘

╔══════════════════════════════════════════════════════════════════════════════════════════════╗
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║

title: AI description: AI 驱动的写作辅助。 docs:

  • route: https://pro.platejs.org/docs/examples/ai title: Plus

<ComponentPreview name="ai-demo" /> <PackageInfo>

功能特性

  • 上下文感知的命令菜单,可适应光标、文本选择和块选择工作流。
  • 流式 Markdown/MDX 插入,支持表格、列和代码块,由 streamInsertChunk 提供支持。
  • 插入和对话审查模式,通过 withAIBatchtf.ai.undo() 实现撤销安全的批处理。
  • 块选择感知的变换,使用 tf.aiChat.replaceSelectiontf.aiChat.insertBelow 替换或追加整个部分。
  • 直接集成 @ai-sdk/react,使 api.aiChat.submit 可以从 Vercel AI SDK 助手流式传输响应。
  • 建议和评论工具,可对比 AI 编辑、接受/拒绝更改,并将 AI 反馈映射回文档范围。
</PackageInfo>

Kit 用法

<Steps>

安装

添加 AI 功能最快的方法是使用 AIKit。它包含配置好的 AIPluginAIChatPlugin、Markdown 流式传输助手、光标覆盖层及其 Plate UI 组件。

<ComponentSource name="ai-kit" />
  • AIMenu:用于提示、工具快捷方式和对话审查的浮动命令界面。
  • AILoadingBar:在编辑器容器中显示流式传输状态。
  • AIAnchorElement:在流式传输期间用于定位浮动菜单的不可见锚点节点。
  • AILeaf:使用微妙样式渲染 AI 标记的文本。

添加 Kit

import { createPlateEditor } from 'platejs/react';
import { AIKit } from '@/components/editor/plugins/ai-kit';

const editor = createPlateEditor({
  plugins: [
    // ...otherPlugins,
    ...AIKit,
  ],
});

添加 API 路由

公开一个流式命令端点,代理你的模型提供商:

<ComponentSource name="ai-api" />

配置环境

在本地设置提供商凭证:

AI_GATEWAY_API_KEY="your-api-key"
</Steps>

手动使用

<Steps>

安装

npm install @platejs/ai @platejs/markdown @platejs/selection @ai-sdk/react ai

@platejs/suggestion 是可选的,但对于基于差异的编辑建议是必需的。

添加插件

import { createPlateEditor } from 'platejs/react';
import { AIChatPlugin, AIPlugin } from '@platejs/ai/react';
import { BlockSelectionPlugin } from '@platejs/selection/react';
import { MarkdownPlugin } from '@platejs/markdown';

export const editor = createPlateEditor({
  plugins: [
    BlockSelectionPlugin,
    MarkdownPlugin,
    AIPlugin,
    AIChatPlugin, // 在下一步中扩展
  ],
});
  • BlockSelectionPlugin:启用多块选择,AIChatPlugin 依赖它来进行插入/替换变换。
  • MarkdownPlugin:提供流式工具使用的 Markdown 序列化。
  • AIPlugin:添加 AI 标记和用于撤销 AI 批处理的变换。
  • AIChatPlugin:提供 AI 组合框、API 助手和变换。

使用 AIPlugin.withComponent 配合你自己的元素(或 AILeaf)来高亮显示 AI 生成的文本。

配置 AIChatPlugin

扩展 AIChatPlugin 以挂接流式传输和编辑。该示例镜像了 AIKit 的核心逻辑,同时保持 UI 无头化。

import { AIChatPlugin, applyAISuggestions, streamInsertChunk, useChatChunk } from '@platejs/ai/react';
import { withAIBatch } from '@platejs/ai';
import { getPluginType, KEYS, PathApi } from 'platejs';
import { usePluginOption } from 'platejs/react';

export const aiChatPlugin = AIChatPlugin.extend({
  options: {
    chatOptions: {
      api: '/api/ai/command',
      body: {
        model: 'openai/gpt-4o-mini',
      },
    },
    trigger: ' ',
    triggerPreviousCharPattern: /^\s?$/,
  },
  useHooks: ({ editor, getOption }) => {
    const mode = usePluginOption(AIChatPlugin, 'mode');
    const toolName = usePluginOption(AIChatPlugin, 'toolName');

    useChatChunk({
      onChunk: ({ chunk, isFirst, text }) => {
        if (mode === 'insert') {
          if (isFirst) {
            editor.setOption(AIChatPlugin, 'streaming', true);

            editor.tf.insertNodes(
              {
                children: [{ text: '' }],
                type: getPluginType(editor, KEYS.aiChat),
              },
              {
                at: PathApi.next(editor.selection!.focus.path.slice(0, 1)),
              }
            );
          }

          if (!getOption('streaming')) return;

          withAIBatch(
            editor,
            () => {
              streamInsertChunk(editor, chunk, {
                textProps: {
                  [getPluginType(editor, KEYS.ai)]: true,
                },
              });
            },
            { split: isFirst }
          );
        }

        if (toolName === 'edit' && mode === 'chat') {
          withAIBatch(
            editor,
            () => {
              applyAISuggestions(editor, text);
            },
            { split: isFirst }
          );
        }
      },
      onFinish: () => {
        editor.setOption(AIChatPlugin, 'streaming', false);
        editor.setOption(AIChatPlugin, '_blockChunks', '');
        editor.setOption(AIChatPlugin, '_blockPath', null);
        editor.setOption(AIChatPlugin, '_mdxName', null);
      },
    });
  },
});
  • useChatChunk:监视 UseChatHelpers 状态并生成增量块。
  • streamInsertChunk:将 Markdown/MDX 流式传输到文档中,尽可能重用现有块。
  • applyAISuggestions:当 toolName === 'edit' 时,将响应转换为临时建议节点。
  • withAIBatch:标记历史批处理,使 tf.ai.undo() 仅撤销最后一次 AI 生成的更改。

当你扩展插件时,提供你自己的 render 组件(工具栏按钮、浮动菜单等)。

构建 API 路由

在服务器上处理 api.aiChat.submit 请求。每个请求都包含来自 @ai-sdk/react 的聊天 messages 和一个 ctx 负载,其中包含编辑器 children、当前 selection 和最后的 toolName完整 API 示例

import { createGateway } from '@ai-sdk/gateway';
import { convertToCoreMessages, streamText } from 'ai';
import { createSlateEditor } from 'platejs';

import { BaseEditorKit } from '@/registry/components/editor/editor-base-kit';
import { markdownJoinerTransform } from '@/registry/lib/markdown-joiner-transform';

export async function POST(req: Request) {
  const { apiKey, ctx, messages, model } = await req.json();

  const editor = createSlateEditor({
    plugins: BaseEditorKit,
    selection: ctx.selection,
    value: ctx.children,
  });

  const gateway = createGateway({
    apiKey: apiKey ?? process.env.AI_GATEWAY_API_KEY!,
  });

  const result = streamText({
    experimental_transform: markdownJoinerTransform(),
    messages: convertToCoreMessages(messages),
    model: gateway(model ?? 'openai/gpt-4o-mini'),
    system: ctx.toolName === 'edit' ? 'You are an editor that rewrites user text.' : undefined,
  });

  return result.toDataStreamResponse();
}
  • ctx.childrenctx.selection 被重新水合到 Slate 编辑器中,以便你可以构建丰富的提示(参见提示模板)。
  • 通过 chatOptions.body 转发提供商设置(模型、apiKey、温度、网关标志等);你添加的所有内容都会在 JSON 负载中原样传递,后端可以在调用 createGateway 前读取。
  • 始终从服务器读取密钥。客户端应仅发送不透明标识符或短期令牌。
  • 返回流式响应,以便 useChatuseChatChunk 可以逐步处理令牌。

连接 useChat

使用 @ai-sdk/react 桥接编辑器和模型端点。在插件上存储助手,以便变换可以重新加载、停止或显示聊天状态。

import { useEffect } from 'react';

import { type UIMessage, DefaultChatTransport } from 'ai';
import { type UseChatHelpers, useChat } from '@ai-sdk/react';
import { AIChatPlugin } from '@platejs/ai/react';
import { useEditorPlugin } from 'platejs/react';

type ChatMessage = UIMessage<{}, { toolName: 'comment' | 'edit' | 'generate'; comment?: unknown }>;

export const useEditorAIChat = () => {
  const { editor, setOption } = useEditorPlugin(AIChatPlugin);

  const chat = useChat<ChatMessage>({
    id: 'editor',
    api: '/api/ai/command',
    transport: new DefaultChatTransport(),
    onData(data) {
      if (data.type === 'data-toolName') {
        editor.setOption(AIChatPlugin, 'toolName', data.data);
      }
    },
  });

  useEffect(() => {
    setOption('chat', chat as UseChatHelpers<ChatMessage>);
  }, [chat, setOption]);

  return chat;
};

将助手与 useEditorChat 结合使用以保持浮动菜单正确锚定:

import { useEditorChat } from '@platejs/ai/react';

useEditorChat({
  chat,
  onOpenChange: (open) => {
    if (!open) chat.stop?.();
  },
});

现在你可以以编程方式提交提示:

import { AIChatPlugin } from '@platejs/ai/react';

editor.getApi(AIChatPlugin).aiChat.submit('', {
  prompt: {
    default: 'Continue the document after {block}',
    selecting: 'Rewrite {selection} with a clearer tone',
  },
  toolName: 'generate',
});
</Steps>

提示模板

客户端提示

  • api.aiChat.submit 接受一个 EditorPrompt。提供一个字符串、一个包含 default/selecting/blockSelecting 的对象,或一个接收 { editor, isSelecting, isBlockSelecting } 的函数。客户端中的 getEditorPrompt 助手将该值转换为最终字符串。
  • 将其与 replacePlaceholders(editor, template, { prompt }) 结合使用,使用 @platejs/ai 生成的 Markdown 展开 {editor}{block}{blockSelection}{prompt}
import { replacePlaceholders } from '@platejs/ai';

editor.getApi(AIChatPlugin).aiChat.submit('Improve tone', {
  prompt: ({ isSelecting }) =>
    isSelecting
      ? replacePlaceholders(editor, 'Rewrite {blockSelection} using a friendly tone.')
      : replacePlaceholders(editor, 'Continue {block} with two more sentences.'),
  toolName: 'generate',
});

服务器端提示

apps/www/src/app/api/ai/command 中的演示后端从 ctx 重构编辑器并构建结构化提示:

  • getChooseToolPrompt 决定请求是 generateedit 还是 comment
  • getGeneratePromptgetEditPromptgetCommentPrompt 将当前编辑器状态转换为针对每种模式量身定制的指令。
  • getMarkdowngetMarkdownWithSelectionbuildStructuredPrompt 这样的实用助手(参见 apps/www/src/app/api/ai/command/prompts.ts)使得将块 ID、选择和 MDX 标签嵌入到 LLM 请求中变得容易。

增强从客户端发送的负载以微调服务器提示:

editor.setOption(aiChatPlugin, 'chatOptions', {
  api: '/api/ai/command',
  body: {
    model: 'openai/gpt-4o-mini',
    tone: 'playful',
    temperature: 0.4,
  },
});

chatOptions.body 下的所有内容都会到达路由处理程序,让你可以交换提供商、传递用户特定的元数据或分支到不同的提示模板。

键盘快捷键

<KeyTable> <KeyTableItem hotkey="Space">在空块中打开 AI 菜单(光标模式)</KeyTableItem> <KeyTableItem hotkey="Cmd + J">显示 AI 菜单(通过 `shortcuts.show` 设置)</KeyTableItem> <KeyTableItem hotkey="Escape">隐藏 AI 菜单并停止流式传输</KeyTableItem> </KeyTable>

流式传输

流式传输工具在响应到达时保持复杂布局完整:

  • streamInsertChunk(editor, chunk, options) 反序列化 Markdown 块,就地更新当前块,并根据需要追加新块。使用 textProps/elementProps 标记流式节点(例如,标记 AI 文本)。
  • streamDeserializeMdstreamDeserializeInlineMd 提供较低级别的访问,如果你需要控制自定义节点类型的流式传输。
  • streamSerializeMd 镜像编辑器状态,以便你可以检测流式内容和响应缓冲区之间的偏差。

当流式传输完成时,重置内部的 _blockChunks_blockPath_mdxName 选项,以便从干净的状态开始下一个响应。

流式传输示例

<ComponentPreview name="markdown-streaming-demo" />

Plate Plus

<ComponentPreviewPro name="ai-pro" />

钩子

useAIChatEditor

为聊天预览注册一个辅助编辑器,并使用块级记忆化反序列化 Markdown。

<API name="useAIChatEditor"> <APIParameters> <APIItem name="editor" type="SlateEditor">专用于聊天预览的编辑器实例。</APIItem> <APIItem name="content" type="string">模型返回的 Markdown 内容。</APIItem> <APIItem name="options" type="DeserializeMdOptions" optional>传递 `parser` 以在反序列化之前过滤标记。</APIItem> </APIParameters> </API>
import { usePlateEditor } from 'platejs/react';
import { MarkdownPlugin } from '@platejs/markdown';
import { AIChatPlugin, useAIChatEditor } from '@platejs/ai/react';

const aiPreviewEditor = usePlateEditor({
  plugins: [MarkdownPlugin, AIChatPlugin],
});

useAIChatEditor(aiPreviewEditor, responseMarkdown, {
  parser: { exclude: ['space'] },
});

useEditorChat

UseChatHelpers 连接到编辑器状态,以便 AI 菜单知道是锚定到光标、选择还是块选择。

<API name="useEditorChat"> <APIParameters> <APIItem name="chat" type="UseChatHelpers&lt;ChatMessage&gt;">由 `useChat` 返回的助手。</APIItem> <APIItem name="onOpenBlockSelection" type="(blocks: NodeEntry[]) => void" optional>当菜单在块选择上打开时调用。</APIItem> <APIItem name="onOpenChange" type="(open: boolean) => void" optional>每当菜单打开或关闭时调用。</APIItem> <APIItem name="onOpenCursor" type="() => void" optional>当菜单在光标处打开时调用。</APIItem> <APIItem name="onOpenSelection" type="() => void" optional>当菜单在文本选择上打开时调用。</APIItem> </APIParameters> </API>

useChatChunk

逐块流式传输聊天响应,并让你完全控制插入。

<API name="useChatChunk"> <APIParameters> <APIItem name="onChunk" type="(chunk: { chunk: string; isFirst: boolean; nodes: TText[]; text: string }) => void">处理每个流式块。</APIItem> <APIItem name="onFinish" type="({ content }: { content: string }) => void" optional>当流式传输完成时调用。</APIItem> </APIParameters> </API>

工具函数

withAIBatch

将编辑器操作分组到单个历史批处理中,并将其标记为 AI 生成,以便 tf.ai.undo() 可以安全地删除它。

<API name="withAIBatch"> <APIParameters> <APIItem name="editor" type="SlateEditor">目标编辑器。</APIItem> <APIItem name="fn" type="() => void">要运行的操作。</APIItem> <APIItem name="options" type="{ split?: boolean }" optional>设置 `split: true` 以开始新的历史批处理。</APIItem> </APIParameters> </API>

applyAISuggestions

将 AI 输出与存储的 chatNodes 进行差异比较,并写入临时建议节点。需要 @platejs/suggestion

<API name="applyAISuggestions"> <APIParameters> <APIItem name="editor" type="SlateEditor">要应用建议的编辑器。</APIItem> <APIItem name="content" type="string">来自模型的 Markdown 响应。</APIItem> </APIParameters> </API>

补充助手允许你完成或丢弃差异:

  • acceptAISuggestions(editor):将临时建议节点转换为永久建议。
  • rejectAISuggestions(editor):删除临时建议节点并清除建议标记。

aiCommentToRange

将流式评论元数据映射回文档范围,以便可以自动插入评论。

<API name="aiCommentToRange"> <APIParameters> <APIItem name="editor" type="PlateEditor">编辑器实例。</APIItem> <APIItem name="options" type="{ blockId: string; comment: string; content: string }">用于定位范围的块 ID 和文本。</APIItem> </APIParameters> <APIReturns type="{ start: BasePoint; end: BasePoint } | null">与评论匹配的范围,如果找不到则为 `null`。</APIReturns> </API>

findTextRangeInBlock

使用 LCS 在块内查找最接近匹配的模糊搜索助手。

<API name="findTextRangeInBlock"> <APIParameters> <APIItem name="node" type="TNode">要搜索的块节点。</APIItem> <APIItem name="searchText" type="string">要定位的文本片段。</APIItem> </APIParameters> <APIReturns type="{ start: { path: Path; offset: number }; end: { path: Path; offset: number } } | null">匹配的范围或 `null`。</APIReturns> </API>

getEditorPrompt

生成尊重光标、选择或块选择状态的提示。

<API name="getEditorPrompt"> <APIParameters> <APIItem name="editor" type="SlateEditor">提供上下文的编辑器。</APIItem> <APIItem name="options" type="{ prompt?: EditorPrompt }">描述提示的字符串、配置或函数。</APIItem> </APIParameters> <APIReturns type="string">上下文化的提示字符串。</APIReturns> </API>

replacePlaceholders

用序列化的 Markdown 替换像 {editor}{blockSelection}{prompt} 这样的占位符。

<API name="replacePlaceholders"> <APIParameters> <APIItem name="editor" type="SlateEditor">提供内容的编辑器。</APIItem> <APIItem name="text" type="string">模板文本。</APIItem> <APIItem name="options" type="{ prompt?: string }" optional>注入到 `{prompt}` 中的提示值。</APIItem> </APIParameters> <APIReturns type="string">占位符被 Markdown 替换后的模板。</APIReturns> </API>

插件

AIPlugin

向流式文本添加 ai 标记,并公开用于删除 AI 节点或撤销最后一个 AI 批处理的变换。使用 .withComponent 来使用自定义组件渲染 AI 标记的文本。

<API name="AIPlugin"> <APIOptions> <APIItem name="node.isLeaf" type="true">AI 内容存储在文本节点上。</APIItem> <APIItem name="node.isDecoration" type="false">AI 标记是常规文本属性,而不是装饰。</APIItem> </APIOptions> </API>

AIChatPlugin

支持 AI 菜单、聊天状态和变换的主要插件。

<API name="AIChatPlugin"> <APIOptions> <APIItem name="trigger" type="RegExp | string | string[]" optional>打开命令菜单的字符。默认为 `' '`。</APIItem> <APIItem name="triggerPreviousCharPattern" type="RegExp" optional>必须与触发器之前的字符匹配的模式。默认为 `/^\s?$/`。</APIItem> <APIItem name="triggerQuery" type="(editor: SlateEditor) => boolean" optional>返回 `false` 以取消在特定上下文中打开。</APIItem> <APIItem name="chat" type="UseChatHelpers&lt;ChatMessage&gt;" optional>存储来自 `useChat` 的助手,以便 API 调用可以访问它们。</APIItem> <APIItem name="chatNodes" type="TIdElement[]" optional>用于差异编辑建议的节点快照(内部管理)。</APIItem> <APIItem name="chatSelection" type="TRange | null" optional>提交提示之前捕获的选择(内部管理)。</APIItem> <APIItem name="mode" type="'chat' | 'insert'">控制响应是直接流式传输到文档中还是打开审查面板。默认为 `'insert'`。</APIItem> <APIItem name="open" type="boolean" optional>AI 菜单是否可见。默认为 `false`。</APIItem> <APIItem name="streaming" type="boolean" optional>响应流式传输时为 true。默认为 `false`。</APIItem> <APIItem name="toolName" type="'comment' | 'edit' | 'generate' | null" optional>用于解释响应的活动工具。</APIItem> </APIOptions> </API>

API

api.aiChat.submit(input, options?)

向模型提供商提交提示。当省略 mode 时,对于折叠的光标默认为 'insert',否则为 'chat'

<API name="submit"> <APIParameters> <APIItem name="input" type="string">来自用户的原始输入。</APIItem> <APIItem name="options" type="object" optional>微调提交行为。</APIItem> </APIParameters> <APIOptions type="object"> <APIItem name="mode" type="'chat' | 'insert'" optional>覆盖响应模式。</APIItem> <APIItem name="options" type="ChatRequestOptions" optional>转发到 `chat.sendMessage`(模型、标头等)。</APIItem> <APIItem name="prompt" type="EditorPrompt" optional>由 `getEditorPrompt` 处理的字符串、配置或函数。</APIItem> <APIItem name="toolName" type="'comment' | 'edit' | 'generate' | null" optional>标记提交,以便钩子可以做出不同的反应。</APIItem> </APIOptions> </API>

api.aiChat.reset(options?)

清除聊天状态,删除 AI 节点,并可选地撤销最后一个 AI 批处理。

<API name="reset"> <APIParameters> <APIItem name="options" type="{ undo?: boolean }" optional>传递 `undo: false` 以保留流式内容。</APIItem> </APIParameters> </API>

api.aiChat.node(options?)

检索与指定条件匹配的第一个 AI 节点。

<API name="node"> <APIParameters> <APIItem name="options" type="EditorNodesOptions &amp; { anchor?: boolean; streaming?: boolean }" optional>设置 `anchor: true` 以获取锚点节点,或 `streaming: true` 以检索当前正在流式传输到的节点。</APIItem> </APIParameters> <APIReturns type="NodeEntry | undefined">匹配的节点条目(如果找到)。</APIReturns> </API>

api.aiChat.reload()

使用存储的 UseChatHelpers 重放最后一个提示,在重新提交之前恢复原始选择或块选择。

api.aiChat.stop()

停止流式传输并调用 chat.stop

api.aiChat.show()

打开 AI 菜单,清除先前的聊天消息,并重置工具状态。

api.aiChat.hide(options?)

关闭 AI 菜单,可选地撤销最后一个 AI 批处理并重新聚焦编辑器。

<API name="hide"> <APIParameters> <APIItem name="options" type="{ focus?: boolean; undo?: boolean }" optional>设置 `focus: false` 以将焦点保持在编辑器外部,或 `undo: false` 以保留插入的内容。</APIItem> </APIParameters> </API>

变换

tf.aiChat.accept()

接受最新响应。在插入模式下,它删除 AI 标记并将插入符号放置在流式内容的末尾。在聊天模式下,它应用待处理的建议。

tf.aiChat.insertBelow(sourceEditor, options?)

在当前选择或块选择下方插入聊天预览(sourceEditor)。

<API name="insertBelow"> <APIParameters> <APIItem name="sourceEditor" type="SlateEditor">包含生成内容的编辑器。</APIItem> <APIItem name="options" type="{ format?: 'all' | 'none' | 'single' }" optional>从源选择复制格式。默认为 `'single'`。</APIItem> </APIParameters> </API>

tf.aiChat.replaceSelection(sourceEditor, options?)

用聊天预览替换当前选择或块选择。

<API name="replaceSelection"> <APIParameters> <APIItem name="sourceEditor" type="SlateEditor">包含生成内容的编辑器。</APIItem> <APIItem name="options" type="{ format?: 'all' | 'none' | 'single' }" optional>控制应应用原始选择的多少格式。</APIItem> </APIParameters> </API>

tf.aiChat.removeAnchor(options?)

删除用于定位 AI 菜单的临时锚点节点。

<API name="removeAnchor"> <APIParameters> <APIItem name="options" type="EditorNodesOptions" optional>过滤要删除的节点。</APIItem> </APIParameters> </API>

tf.ai.insertNodes(nodes, options?)

在当前选择(或 options.target)处插入标记有 AI 标记的节点。

tf.ai.removeMarks(options?)

从匹配节点中清除 AI 标记。

tf.ai.removeNodes(options?)

删除标记为 AI 生成的文本节点。

tf.ai.undo()

如果最新的历史条目是由 withAIBatch 创建的并包含 AI 内容,则撤销它。清除配对的重做条目以避免重新应用 AI 输出。

自定义

添加自定义 AI 命令

<ComponentSource name="ai-menu" />

扩展 aiChatItems 映射以添加新命令。每个命令都接收 { aiEditor, editor, input },并可以使用自定义提示或变换调度 api.aiChat.submit

简单自定义命令

summarizeInBullets: {
  icon: <ListIcon />,
  label: 'Summarize in bullets',
  value: 'summarizeInBullets',
  onSelect: ({ editor }) => {
    void editor.getApi(AIChatPlugin).aiChat.submit('', {
      prompt: 'Summarize the current selection using bullet points',
      toolName: 'generate',
    });
  },
},

具有复杂逻辑的命令

generateTOC: {
  icon: <BookIcon />,
  label: 'Generate table of contents',
  value: 'generateTOC',
  onSelect: ({ editor }) => {
    const headings = editor.api.nodes({
      match: (n) => ['h1', 'h2', 'h3'].includes(n.type as string),
    });

    const prompt =
      headings.length === 0
        ? 'Create a realistic table of contents for this document'
        : 'Generate a table of contents that reflects the existing headings';

    void editor.getApi(AIChatPlugin).aiChat.submit('', {
      mode: 'insert',
      prompt,
      toolName: 'generate',
    });
  },
},

菜单会自动在命令和建议状态之间切换:

  • cursorCommand:光标已折叠且尚无响应。
  • selectionCommand:已选择文本且尚无响应。
  • cursorSuggestion / selectionSuggestion:存在响应,因此会显示接受、重试或在下方插入等操作。

使用 toolName('generate' | 'edit' | 'comment')来控制流式钩子如何处理响应。例如,'edit' 启用基于差异的建议,而 'comment' 允许你使用 aiCommentToRange 将流式评论转换为讨论线程。

║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
║
╚══════════════════════════════════════════════════════════════════════════════════════════════╝

← Root | ↑ Up