
Plate 如何用 Copilot 添加打字时的幽灵文本 AI 补全【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate如果你的 Plate 编辑器里还缺少边打字边预测下一段文字的能力Copilot 插件就是为此设计的它在光标所在块末尾之后渲染一段灰色幽灵文本ghost text你可以用 Tab 整段接受、用Cmd→逐词接受或用 Escape 拒绝。功能来自platejs/ai包里的CopilotPlugin配合platejs/markdown把编辑器内容序列化成提示词再经由你自己的后端 API 路由调用 Vercel AI SDK 的补全接口。下面的路径基于 Next.js 项目API 路由示例使用app/api/...目录约定完成后的效果是在段落末尾敲空格后自动出现补全建议或按CtrlSpace手动触发。两种接法Kit 与手动配置Plate 提供两条安装路径任选其一。快速路径CopilotKitKit 自带预配置的CopilotPlugin、MarkdownKit和 Plate UI 组件包括渲染幽灵文本的GhostText组件import { createPlateEditor } from platejs/react; import { CopilotKit } from /components/editor/plugins/copilot-kit; const editor createPlateEditor({ plugins: [ // ...otherPlugins, ...CopilotKit, // 使用 Tab 的插件要放在 CopilotKit 之后避免冲突 // IndentPlugin, // TabbablePlugin, ], });手动路径单独安装依赖并逐个挂载插件npm install platejs/ai platejs/markdownimport { CopilotPlugin } from platejs/ai/react; import { MarkdownPlugin } from platejs/markdown; import { createPlateEditor } from platejs/react; const editor createPlateEditor({ plugins: [ // ...otherPlugins, MarkdownPlugin, CopilotPlugin, // 使用 Tab 的插件要放在 CopilotPlugin 之后避免冲突 // IndentPlugin, // TabbablePlugin, ], });两个插件的分工是MarkdownPlugin负责把编辑器内容序列化为 Markdown 作为提示词发送CopilotPlugin负责补全逻辑本身。注意插件顺序是硬性要求——CopilotPlugin用 Tab 接受建议如果你同时挂载了IndentPlugin或TabbablePlugin必须把 Copilot 放在它们之前否则 Tab 行为会互相抢占。Kit 路径下补全接口走预配置的 API 路由文档中的copilot-api组件模板手动路径则需要自己创建路由并配置插件选项见下一节。配置 CopilotPlugin接口、快捷键与幽灵文本组件手动接法的核心是CopilotPlugin.configure它决定了调哪个接口、多久自动触发一次、建议用哪个组件渲染、以及哪些快捷键生效import { CopilotPlugin } from platejs/ai/react; import { serializeMd, stripMarkdown } from platejs/markdown; import { GhostText } from /components/ui/ghost-text; const plugins [ // ...otherPlugins, MarkdownPlugin.configure({ options: { remarkPlugins: [remarkMath, remarkGfm, remarkMdx], }, }), CopilotPlugin.configure(({ api }) ({ options: { completeOptions: { api: /api/ai/copilot, onError: () { // Mock the API response. Remove when you implement the route /api/ai/copilot api.copilot.setBlockSuggestion({ text: stripMarkdown(This is a mock suggestion.), }); }, onFinish: (_, completion) { if (completion 0) return; api.copilot.setBlockSuggestion({ text: stripMarkdown(completion), }); }, }, debounceDelay: 500, renderGhostText: GhostText, }, shortcuts: { accept: { keys: tab }, acceptNextWord: { keys: modright }, reject: { keys: escape }, triggerSuggestion: { keys: ctrlspace }, }, })), ];各选项的用途completeOptions对应 Vercel AI SDKuseCompletionhook 的配置。api指定补全接口地址onError在请求失败时回调文档用它在开发阶段 mock 一条建议接口实现后应移除该 mockonFinish拿到补全文本后调用api.copilot.setBlockSuggestion把它设为当前块的幽灵文本completion 0表示模型认为无法续写直接忽略。debounceDelay自动触发前的防抖毫秒数默认0不防抖。renderGhostText渲染幽灵文本的 React 组件。仓库中的 ghost-text 组件实现 读取CopilotPlugin的isSuggested/suggestionText插件状态无建议时返回null有建议时输出一段pointer-events-none、contentEditable{false}的灰色 span保证建议文本不可被光标选中或编辑。shortcutstab整段接受对应 transformtf.copilot.accept()、modright逐词接受tf.copilot.acceptNextWord()、escape拒绝并重置插件状态api.copilot.reject()、ctrlspace手动触发一次建议请求api.copilot.triggerSuggestion()。自动触发debatnce 模式在段落末尾输入空格后生效默认triggerQuery只检查两点选区未展开、选区位于块末尾。autoTriggerQuery的默认条件是上一块非空、上一块以空格结尾、且当前没有已存在的建议。添加服务端补全路由Copilot 需要你自己的 API 路由中转模型请求。在app/api/ai/copilot/route.ts创建 POST 处理器import type { NextRequest } from next/server; import { createGateway, generateText } from ai; import { NextResponse } from next/server; export async function POST(req: NextRequest) { const { apiKey: key, instructions, model gpt-4o-mini, prompt, } await req.json(); const apiKey typeof key string ? key.trim() : ; if (!apiKey) { return NextResponse.json( { error: Missing AI Gateway API key. }, { status: 401 } ); } const gateway createGateway({ apiKey }); try { const result await generateText({ abortSignal: req.signal, instructions, maxOutputTokens: 50, model: gateway(openai/${model}), prompt, temperature: 0.7, }); return NextResponse.json(result); } catch (error) { if (error instanceof Error error.name AbortError) { return NextResponse.json(null, { status: 408 }); } return NextResponse.json( { error: Failed to process AI request }, { status: 500 } ); } }这个路由从请求体里解出apiKey、instructions、model默认gpt-4o-mini和prompt通过createGateway({ apiKey })创建 AI Gateway 客户端后调用generateTextmaxOutputTokens限制为 50temperature为 0.7。缺少 key 返回 401客户端中断返回 408其他错误返回 500。密钥从哪来BYOK自带密钥场景下用户在编辑器设置里填入 AI Gateway key浏览器把它作为completeOptions.body中的apiKey发给路由仅本次请求使用。如果改用共享的应用级凭据则应在服务端加载 key、对每个请求做认证鉴权和按用户的用量限制并且绝不能把共享 key 写进客户端代码或请求体。验证补全链路文档给出的验证方式是分层的接口未实现时配置中的onError回调会 mock 一条This is a mock suggestion.示例结果。此时如果你敲空格或按CtrlSpace后光标后面出现这段灰色文本说明幽灵文本渲染链路setBlockSuggestion→renderGhostText已经通了。接口实现后移除onError里的 mock。请求成功时onFinish收到补全文本setBlockSuggestion将其经stripMarkdown处理后设为建议模型返回0表示无续写界面不出现幽灵文本。交互验证建议出现后按 Tab 应整段接受进正文按Cmd→应只接受下一个词按 Escape 应清除建议。自定义换模型、改触发条件切换模型模型在 API 路由侧配置也可以让客户端通过body.model指定CopilotPlugin.configure(({ api }) ({ options: { completeOptions: { api: /api/ai/copilot, body: { instructions: Continue the current paragraph in the same tone., model: anthropic/claude-3-haiku-20240307, }, }, // ... other options }, })),路由侧则把gateway(openai/ model)改成gateway(model)让模型标识含 provider 前缀由请求体决定。更多 provider 与模型以 Vercel AI SDK 的文档为准。改触发条件用triggerQuery/autoTriggerQuery两个函数控制何时允许触发和何时自动触发。文档示例只在段落块type ! p时直接false里触发且自动触发要求选区在块尾、无展开选区triggerQuery: ({ editor }) { // Only trigger in paragraph blocks const block editor.api.block(); if (!block || block[0].type ! p) return false; return editor.selection !editor.api.isExpanded() editor.api.isAtEnd(); }, autoTriggerQuery: ({ editor }) { const block editor.api.block(); if (!block) return false; const text editor.api.string(block[0]); // Trigger after question words return /\b(what|how|why|when|where)\s*$/i.test(text); },另外两个可调项body.instructions定义 AI 的角色与行为文档示例要求续写到下一个标点为止、保持语气、不新起块、无法续写时返回0getPrompt决定发送哪些上下文默认取祖先节点的 Markdown 序列化可用serializeMd自定义例如只取最高层块并包在Continue the text up to the next punctuation mark:模板里。边界与限制Tab 冲突是最常见的坑Copilot 用 Tab 接受建议任何其他占用 Tab 的插件IndentPlugin、TabbablePlugin等必须排在 CopilotKit/CopilotPlugin 之后。maxOutputTokens: 50与示例路由中续写到下一个标点的 instructions 是配套的补全被刻意限制得短小而不是生成整段。文档建议对路由做输入校验如 prompt 长度上限、限流和内容过滤但示例中的rateLimit(req)与containsSensitiveContent(prompt)只是占位注释需要你自行实现。运行中如需中断api.copilot.stop()会取消防抖触发、abort 当前请求并重置 abort controller。完整选项与 transform 定义见 Copilot 文档/(ai)/copilot.mdx)包括CopilotPlugin各选项getPrompt默认值、triggerQuery默认值等和setBlockSuggestion的参数说明text必填id可选默认作用于当前块。【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考