kc-plate-editor
v0.6.1
Published
A powerful and customizable rich text editor built on Plate
Maintainers
Readme
KC Plate Editor
基于 Plate.js 构建的功能丰富的富文本编辑器 SDK。
私有项目 - 本项目仅供内部使用,未经授权不得分发。
版本信息
| 版本 | 状态 | 说明 | |------|------|------| | v0.6.1 | ✅ 最新版本 | Bug 修复、代码质量、单元测试、依赖升级 | | v0.6.0 | ✅ 稳定版本 | 全面升级:AI SDK 6.x、Vite 7.x、依赖更新 | | v0.5.8 | ✅ 稳定版本 | 内容格式转换优化(官方 API) | | v0.5.6 | ✅ 稳定版本 | 增强上传配置灵活性 | | v0.5.5 | ✅ 稳定版本 | 快捷键保存、本地存储、目录跳转优化 | | v0.5.0 | ✅ 已发布私服 | 基础稳定版本 |
v0.6.0 更新内容
- 依赖全面升级 - 更新至最新稳定版本
ai5.0.28 → 6.0.45(重大升级)@ai-sdk/react2.0.28 → 3.0.47(重大升级)vite5.4.11 → 7.3.1(重大升级)platejs52.0.1 → 52.0.17@platejs/ai52.0.6 → 52.1.0@platejs/markdown52.0.4 → 52.1.0typescript5.9.2 → 5.9.3tailwindcss4.1.13 → 4.1.18zod4.1.12 → 4.3.5zustand5.0.8 → 5.0.10
- 代码精简 - 移除 use-chat.ts 中约 1300 行的测试代码
- 配置清理 - 移除废弃配置属性(uploadToken, authorization)
- 类型优化 - 优化插件类型定义,改进 EditorPluginKit 类型
- HTTP 工具优化 - 简化请求头合并逻辑,移除兼容代码
- 导出顺序修复 - 修复 package.json exports 中 types 字段顺序
v0.5.8 更新内容
- 内容格式转换 - 基于 Plate.js 官方 API 实现 JSON/HTML/Markdown 互转
- 统一内容接口 - 新增
getContentAs()和setContentFrom()方法 - 官方 serializeHtml - HTML 序列化使用官方静态渲染 API
- 官方 MarkdownPlugin - Markdown 转换使用官方
@platejs/markdown - 代码清理 - 移除旧的 DOM 清理方案,统一使用官方 API
v0.5.6 更新内容
- 增强上传配置 - 新增
UploadConfig详细配置接口 - 增强 AI 配置 - 新增
AIConfig详细配置接口 - 自定义上传函数 - 支持
customUpload完全自定义上传逻辑 - 自定义 AI 函数 - 支持
customCopilot和customCommand自定义 AI 逻辑 - 文件验证 - 支持
maxSize和acceptTypes文件验证 - 额外表单数据 - 支持
extraData添加额外表单字段 - 灵活响应解析 - 支持多种服务端响应格式
- 配置辅助函数 - 新增
getUploadConfig、getAiConfig等辅助函数
v0.5.5 更新内容
- 快捷键保存 - 支持
Ctrl+S/Cmd+S快捷键保存 - 本地存储 - 新增
saveToLocalStorage()和clearLocalStorage()方法 - 目录跳转优化 - 修复目录点击跳转不准确的问题
- onSave 回调 - 新增
onSave属性用于处理快捷键保存事件
v0.5.4 更新内容
- 修复编辑器容器双滚动条问题
- 优化
EditorContainer的demo变体高度设置 - 修复工具栏吸顶在嵌套滚动容器中失效的问题
v0.5.0 更新内容
- 优化字数统计、查找替换、目录功能,使用官方 Plate.js API
- 新增公共工具函数
text-utils.ts - 统一常量管理(
HEADING_DEPTH、FONT_STACKS等) - 重构示例页面为模块化结构
- 删除重复的演示页面,整合到 Examples
目录
特性
核心功能
- 富文本编辑 - 标题、列表、表格、代码块等
- AI 辅助 - 内置 AI 写作助手
- 拖拽排序 - 块级元素拖拽
- Markdown - 导入/导出 Markdown
- 多媒体 - 图片、视频、嵌入内容
- 协作 - 评论和建议模式
- 国际化 - 中英文支持
- 响应式 - 自适应工具栏
工具栏系统
- 可配置工具栏 - 灵活的工具栏配置
- 预设模板 - 完整/基础/极简/文档等预设
- 细粒度控制 - 精确控制每个功能
扩展功能
- 查找替换 - 支持大小写敏感
- 字数统计 - 中英文混合统计
- 文档大纲 - 自动生成导航
- 导出功能 - HTML/Markdown/PDF/图片
- 目录功能 - 左侧/右侧目录面板
- 自定义按钮 - 扩展工具栏
安装
从私服安装
# 配置私服(如果尚未配置)
npm config set registry https://your-private-registry.com
# 安装
npm install kc-plate-editor
# 或
pnpm add kc-plate-editor对等依赖
npm install react react-dom platejs快速开始
1. 引入样式
import 'kc-plate-editor/style.css';2. 基础用法
import { PlateEditor } from 'kc-plate-editor';
function MyEditor() {
return <PlateEditor />;
}3. 受控模式
import { useState } from 'react';
import { PlateEditor } from 'kc-plate-editor';
import type { Value } from 'platejs';
function MyEditor() {
const [value, setValue] = useState<Value>([
{ type: 'p', children: [{ text: '开始编辑...' }] },
]);
return (
<PlateEditor
value={value}
onChange={setValue}
/>
);
}4. 使用 Ref 访问 API
import { useRef } from 'react';
import { PlateEditor, type PlateEditorRef } from 'kc-plate-editor';
function MyEditor() {
const editorRef = useRef<PlateEditorRef>(null);
const handleSave = () => {
const content = editorRef.current?.getContent();
console.log('保存内容:', content);
};
return (
<>
<button onClick={handleSave}>保存</button>
<PlateEditor ref={editorRef} />
</>
);
}5. 配置提供者
import { ConfigProvider, PlateEditor } from 'kc-plate-editor';
function App() {
return (
<ConfigProvider
config={{
locale: 'zh-CN',
uploadApiUrl: '/api/upload',
aiApiUrl: '/api/ai/copilot',
customHeaders: {
'Authorization': 'Bearer token',
},
}}
>
<PlateEditor />
</ConfigProvider>
);
}6. 高级上传配置 (v0.5.6+)
import { ConfigProvider, PlateEditor, type UploadConfig } from 'kc-plate-editor';
// 详细的上传配置
const uploadConfig: UploadConfig = {
url: '/api/upload', // 上传地址
fieldName: 'file', // 文件字段名
maxSize: 10 * 1024 * 1024, // 最大 10MB
acceptTypes: ['image/*', 'video/*', 'application/pdf'],
headers: {
'X-Upload-Token': 'your-token',
},
extraData: {
category: 'document',
source: 'editor',
},
};
function App() {
return (
<ConfigProvider
config={{
baseURL: 'https://api.example.com', // API 基础地址
upload: uploadConfig,
locale: 'zh-CN',
}}
>
<PlateEditor />
</ConfigProvider>
);
}7. 自定义上传函数
import { ConfigProvider, PlateEditor, type UploadResult } from 'kc-plate-editor';
// 完全自定义上传逻辑
async function customUpload(
file: File,
onProgress?: (progress: number) => void
): Promise<UploadResult> {
const formData = new FormData();
formData.append('file', file);
// 使用你自己的上传逻辑
const response = await fetch('/api/my-upload', {
method: 'POST',
body: formData,
});
const result = await response.json();
return {
url: result.fileUrl,
name: file.name,
size: file.size,
type: file.type,
key: result.fileId,
};
}
function App() {
return (
<ConfigProvider
config={{
upload: {
customUpload, // 使用自定义上传函数
},
locale: 'zh-CN',
}}
>
<PlateEditor />
</ConfigProvider>
);
}8. AI 配置 (v0.5.6+)
import { ConfigProvider, PlateEditor, type AIConfig } from 'kc-plate-editor';
// 详细的 AI 配置
const aiConfig: AIConfig = {
copilotUrl: '/api/ai/copilot', // Copilot 自动补全 API
commandUrl: '/api/ai/command', // AI 命令/对话 API
apiKey: 'your-api-key', // API 密钥
model: 'gpt-4', // 模型名称
headers: {
'X-Custom-Header': 'value',
},
};
function App() {
return (
<ConfigProvider
config={{
baseURL: 'https://api.example.com',
ai: aiConfig,
locale: 'zh-CN',
}}
>
<PlateEditor />
</ConfigProvider>
);
}集成注意事项
⚠️ 重要: 在将 kc-plate-editor 集成到现有项目时,请仔细阅读以下注意事项,避免常见问题。
1. 全局样式冲突
问题: 项目中的全局 CSS 可能会影响编辑器的显示效果。
常见冲突:
/* ❌ 这些全局样式会破坏编辑器的列表显示 */
ul, ol {
list-style: none;
padding: 0;
margin: 0;
}
/* ❌ 这会影响编辑器内的链接样式 */
a {
text-decoration: none;
color: inherit;
}解决方案:
/* ✅ 方案1: 使用更具体的选择器,排除编辑器区域 */
ul:not([data-slate-node]), ol:not([data-slate-node]) {
list-style: none;
}
/* ✅ 方案2: 在编辑器容器内重置样式 */
[data-plate-editor-container] ul,
[data-plate-editor-container] ol {
list-style: revert;
padding: revert;
margin: revert;
}
/* ✅ 方案3: 直接注释掉全局样式(推荐) */
/* ul, ol { list-style: none; } */2. 容器高度和滚动
问题: 编辑器需要明确的高度才能正确滚动。
// ❌ 错误:没有设置高度,编辑器会无限增长
<div>
<PlateEditor />
</div>
// ✅ 正确:设置固定高度或使用 flex 布局
<div style={{ height: 500 }}>
<PlateEditor />
</div>
// ✅ 正确:使用 flex 布局填充可用空间
<div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
<header>...</header>
<div style={{ flex: 1, overflow: 'hidden' }}>
<PlateEditor />
</div>
</div>Ant Design Card 中使用:
// ✅ 推荐配置:设置 body 的高度和 overflow
<Card
style={{ minHeight: 500 }}
styles={{
body: {
padding: 0,
height: 500,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden' // 防止双滚动条
}
}}
>
<PlateEditor />
</Card>3. 工具栏吸顶
问题: 工具栏吸顶需要正确的滚动容器。
// ❌ 错误:外层容器有 overflow: auto,会创建新的滚动上下文
<div style={{ overflow: 'auto', height: 500 }}>
<PlateEditor toolbarSticky={true} />
</div>
// ✅ 正确:外层容器使用 overflow: hidden
<div style={{ overflow: 'hidden', height: 500 }}>
<PlateEditor toolbarSticky={true} />
</div>独立工具栏场景:
// 使用 StandaloneToolbar 时,需要自己管理滚动容器
import { StandaloneToolbar, PlateEditor } from 'kc-plate-editor';
// 滚动容器
<div className="scroll-container" style={{ overflow: 'auto', height: 500 }}>
{/* 工具栏使用 CSS sticky */}
<div style={{ position: 'sticky', top: 0, zIndex: 50 }}>
<StandaloneToolbar config={config} editor={editor} sticky={false} />
</div>
<PlateEditor showToolbar={false} />
</div>4. 快捷键保存
v0.5.5 新增: 支持 Ctrl+S / Cmd+S 快捷键保存。
<PlateEditor
// 方式1: 使用 onSave 回调处理保存逻辑
onSave={(content) => {
console.log('快捷键保存:', content);
// 调用 API 保存到服务器
saveToServer(content);
}}
// 方式2: 启用自动保存到 localStorage
autoSave={true}
autoSaveKey="my-editor-content"
autoSaveInterval={5000} // 5秒自动保存一次
/>清理本地存储:
// 发布或退出时清理临时数据
const handlePublish = async () => {
await publishDocument();
editorRef.current?.clearLocalStorage();
};
const handleExit = () => {
editorRef.current?.clearLocalStorage();
navigate('/');
};5. 目录跳转
问题: 目录点击跳转不到正确位置。
原因: 编辑器使用了嵌套滚动容器,scrollIntoView 可能无法正确工作。
v0.5.5 已修复: 目录跳转现在会自动查找正确的滚动容器。
// 如果仍有问题,确保编辑器容器结构正确
<div style={{ height: 500, overflow: 'hidden' }}>
<PlateEditor
showToc={true}
tocPosition="right"
tocWidth={240}
/>
</div>6. 表单集成
在 Ant Design Form 中使用:
import { Form, Input, Button } from 'antd';
import { PlateEditor, ConfigProvider, EditorErrorBoundary } from 'kc-plate-editor';
function ArticleForm() {
const editorRef = useRef<PlateEditorRef>(null);
const handleSubmit = async () => {
const content = editorRef.current?.getContent();
const markdown = editorRef.current?.getMarkdown();
const html = await editorRef.current?.getHtml();
// 提交到服务器
await submitArticle({ content, markdown, html });
};
return (
<Form onFinish={handleSubmit}>
<Form.Item label="标题" name="title">
<Input />
</Form.Item>
<Form.Item label="内容">
{/* 添加边框样式 */}
<div style={{ border: '1px solid #d9d9d9', borderRadius: 8, overflow: 'hidden' }}>
<ConfigProvider config={{ locale: 'zh-CN' }}>
<EditorErrorBoundary>
<PlateEditor
ref={editorRef}
toolbarConfig={BASIC_TOOLBAR}
toolbarSticky={false} // 表单中不需要吸顶
showSettings={false}
/>
</EditorErrorBoundary>
</ConfigProvider>
</div>
</Form.Item>
<Button type="primary" htmlType="submit">提交</Button>
</Form>
);
}7. 全屏编辑模式
实现全屏沉浸式编辑:
function FullScreenEditor() {
return (
<div style={{
height: '100vh',
display: 'flex',
flexDirection: 'column',
background: '#fff',
}}>
{/* 顶部导航栏 */}
<div style={{
padding: '8px 16px',
borderBottom: '1px solid #e8e8e8',
flexShrink: 0,
}}>
<span>文档标题</span>
<Button onClick={handleSave}>保存</Button>
</div>
{/* 编辑器区域 - flex: 1 填充剩余空间 */}
<div style={{ flex: 1, overflow: 'hidden' }}>
<PlateEditor
toolbarConfig={FULL_TOOLBAR}
toolbarSticky={true}
toolbarAlign="center"
contentMaxWidth={900}
showToc={true}
tocPosition="right"
autoFocus
/>
</div>
</div>
);
}8. 错误边界
始终使用 EditorErrorBoundary 包裹编辑器:
import { EditorErrorBoundary, PlateEditor } from 'kc-plate-editor';
// ✅ 推荐:使用错误边界防止编辑器崩溃影响整个页面
<EditorErrorBoundary>
<PlateEditor />
</EditorErrorBoundary>9. 性能优化
避免不必要的重渲染:
// ❌ 错误:每次渲染都创建新的配置对象
<PlateEditor
toolbarConfig={{ ...FULL_TOOLBAR, history: false }}
/>
// ✅ 正确:使用 useMemo 缓存配置
const toolbarConfig = useMemo(() => ({
...FULL_TOOLBAR,
history: false,
}), []);
<PlateEditor toolbarConfig={toolbarConfig} />预设切换时重新挂载编辑器:
// 使用 key 强制重新挂载,确保工具栏配置正确应用
<PlateEditor
key={`editor-${activePreset}`}
toolbarConfig={getToolbarConfig()}
/>10. 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|------|----------|----------|
| 列表没有项目符号 | 全局 CSS 重置了 list-style | 移除或修改全局样式 |
| 双滚动条 | 容器和编辑器都有 overflow | 外层使用 overflow: hidden |
| 工具栏不吸顶 | 滚动容器不正确 | 检查父容器的 overflow 设置 |
| 目录跳转失效 | 嵌套滚动容器 | 升级到 v0.5.5 |
| 快捷键无效 | 焦点不在编辑器 | 确保编辑器已聚焦 |
| 内容丢失 | 未保存就离开 | 使用 autoSave 或 onSave |
工具栏配置
使用预设
import {
PlateEditor,
FULL_TOOLBAR, // 完整工具栏
BASIC_TOOLBAR, // 基础工具栏
MINIMAL_TOOLBAR, // 极简工具栏
DOCUMENT_TOOLBAR, // 文档工具栏
} from 'kc-plate-editor';
<PlateEditor toolbarConfig={FULL_TOOLBAR} />
<PlateEditor toolbarConfig={BASIC_TOOLBAR} />
<PlateEditor toolbarConfig={MINIMAL_TOOLBAR} />自定义配置
const customToolbar: ToolbarConfig = {
history: true,
textFormat: ['bold', 'italic', 'underline', 'strikethrough', 'code'],
color: ['textColor', 'backgroundColor', 'highlight'],
align: ['left', 'center', 'right', 'justify'],
list: ['bulleted', 'numbered', 'todo'],
insert: ['link', 'image', 'table', 'emoji'],
typography: ['fontSize', 'lineHeight'],
blockOps: ['indent', 'outdent'],
advanced: ['export', 'import'],
};
<PlateEditor toolbarConfig={customToolbar} />工具栏样式
<PlateEditor
toolbarAlign="center"
toolbarFullWidth={true}
toolbarClassName="custom-class"
toolbarStyle={{ padding: '8px' }}
/>内容操作
获取/设置内容
// 获取 JSON 内容
const value = editorRef.current?.getContent();
// 设置 JSON 内容
editorRef.current?.setContent([
{ type: 'h1', children: [{ text: '标题' }] },
{ type: 'p', children: [{ text: '段落内容' }] },
]);
// 重置内容
editorRef.current?.resetContent();Markdown 转换
// 获取 Markdown
const markdown = editorRef.current?.getMarkdown();
// 设置 Markdown
editorRef.current?.setMarkdown('# 标题\n\n这是段落内容');
// 插入 Markdown
editorRef.current?.insertMarkdown('**粗体文本**');HTML 转换
// 获取 HTML
const html = await editorRef.current?.getHtml();
// 设置 HTML
editorRef.current?.setHtml('<h1>标题</h1><p>段落内容</p>');查找替换
// 查找文本
const matches = editorRef.current?.find('搜索文本', {
caseSensitive: false,
});
console.log(`找到 ${matches.length} 处匹配`);
// 替换文本
const count = editorRef.current?.replace('旧文本', '新文本', {
caseSensitive: false,
replaceAll: true,
});
console.log(`已替换 ${count} 处`);导出功能
// 导出为 HTML 文件
await editorRef.current?.exportAsHtml('document.html');
// 导出为 Markdown 文件
await editorRef.current?.exportAsMarkdown('document.md');
// 导出为 PDF 文件(支持多页)
await editorRef.current?.exportAsPdf('document.pdf');
// 导出为图片(高清 2x)
await editorRef.current?.exportAsImage('document.png');高级功能
字数统计
const wordCount = editorRef.current?.getWordCount();
console.log({
words: wordCount.words,
charactersWithSpaces: wordCount.charactersWithSpaces,
paragraphs: wordCount.paragraphs,
lines: wordCount.lines,
});文档大纲
const outline = editorRef.current?.getOutline();
// 返回: [{ id, depth, title, path, type }]目录功能
<PlateEditor
showToc={true}
tocPosition="left"
tocWidth="240px"
/>编辑器模式
<PlateEditor
mode="edit" // 'edit' | 'view' | 'suggestion'
/>API 参考
PlateEditorRef 方法
内容管理
| 方法 | 说明 | 返回值 |
|------|------|--------|
| getContent() | 获取编辑器内容 | Value |
| setContent(value) | 设置编辑器内容 | void |
| resetContent() | 重置为初始内容 | void |
| getText() | 获取纯文本内容 | string |
| isEmpty() | 检查编辑器是否为空 | boolean |
Markdown/HTML 转换
| 方法 | 说明 | 返回值 |
|------|------|--------|
| getMarkdown() | 获取 Markdown | string |
| setMarkdown(md) | 设置 Markdown | void |
| insertMarkdown(md) | 插入 Markdown | void |
| getHtml() | 获取 HTML | Promise<string> |
| setHtml(html) | 设置 HTML | void |
查找替换
| 方法 | 说明 | 返回值 |
|------|------|--------|
| find(text, options) | 查找文本 | MatchPosition[] |
| replace(search, replace, options) | 替换文本 | number |
文档分析
| 方法 | 说明 | 返回值 |
|------|------|--------|
| getWordCount() | 获取字数统计 | WordCountResult |
| getOutline() | 获取文档大纲 | OutlineItem[] |
导出功能
| 方法 | 说明 | 返回值 |
|------|------|--------|
| exportAsHtml(filename) | 导出 HTML | Promise<void> |
| exportAsMarkdown(filename) | 导出 Markdown | Promise<void> |
| exportAsPdf(filename) | 导出 PDF | Promise<void> |
| exportAsImage(filename) | 导出图片 | Promise<void> |
插入操作
| 方法 | 说明 | 返回值 |
|------|------|--------|
| insertText(text) | 插入文本 | void |
| insertLink(url, text) | 插入链接 | void |
| insertImage(url) | 插入图片 | void |
格式控制
| 方法 | 说明 | 返回值 |
|------|------|--------|
| toggleMark(mark) | 切换格式 | void |
| undo() | 撤销 | void |
| redo() | 重做 | void |
编辑器控制
| 方法 | 说明 | 返回值 |
|------|------|--------|
| focus() | 聚焦编辑器 | void |
| blur() | 失焦编辑器 | void |
| getEditor() | 获取底层编辑器实例 | TPlateEditor |
修改状态
| 方法 | 说明 | 返回值 |
|------|------|--------|
| isModified() | 检查内容是否已修改 | boolean |
| resetModifiedState() | 重置修改状态 | void |
工具栏/目录控制
| 方法 | 说明 | 返回值 |
|------|------|--------|
| setToolbarVisible(visible) | 设置工具栏可见性 | void |
| setTocVisible(visible) | 设置目录可见性 | void |
本地存储 (v0.5.5+)
| 方法 | 说明 | 返回值 |
|------|------|--------|
| saveToLocalStorage() | 手动保存到 localStorage | void |
| clearLocalStorage() | 清理 localStorage 临时数据 | void |
自动保存
| 方法 | 说明 | 返回值 |
|------|------|--------|
| getAutoSaveState() | 获取自动保存状态 | AutoSaveState |
| saveNow() | 立即触发保存 | void |
| restoreAutoSave() | 恢复自动保存的内容 | Value \| null |
| clearAutoSave() | 清除自动保存数据 | void |
PlateEditorProps
基础属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| value | Value | - | 编辑器内容 |
| onChange | (value: Value) => void | - | 内容变化回调 |
| mode | 'edit' \| 'view' \| 'suggestion' | 'edit' | 编辑器模式 |
| disabled | boolean | false | 是否禁用 |
| readOnly | boolean | false | 是否只读 |
| autoFocus | boolean | false | 是否自动聚焦 |
| className | string | - | 自定义类名 |
工具栏属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| toolbarConfig | ToolbarConfig | FULL_TOOLBAR | 工具栏配置 |
| showToolbar | boolean | true | 是否显示工具栏 |
| toolbarSticky | boolean | true | 工具栏是否吸顶 |
| toolbarAlign | 'left' \| 'center' \| 'right' | 'left' | 工具栏对齐 |
| toolbarFullWidth | boolean | false | 工具栏是否全宽 |
| toolbarClassName | string | - | 工具栏类名 |
| toolbarStyle | CSSProperties | - | 工具栏样式 |
| customToolbarButtons | CustomToolbarButtons | - | 自定义工具栏按钮 |
目录属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| showToc | boolean | false | 是否显示目录 |
| tocPosition | 'left' \| 'right' \| 'top' \| 'custom' | 'left' | 目录位置 |
| tocWidth | string \| number | '320px' | 目录宽度 |
| tocClassName | string | - | 目录类名 |
| tocStyle | CSSProperties | - | 目录样式 |
| renderToc | (toc: ReactNode) => ReactNode | - | 自定义目录渲染 |
保存属性 (v0.5.5+)
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| onSave | (content: Value) => void | - | 快捷键保存回调 |
| autoSave | boolean | false | 是否启用自动保存 |
| autoSaveKey | string | - | localStorage 存储键名 |
| autoSaveInterval | number | 30000 | 自动保存间隔(毫秒) |
| onAutoSave | (content: Value) => void | - | 自动保存回调 |
其他属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| showSettings | boolean | true | 是否显示设置按钮 |
| contentMaxWidth | string \| number | - | 内容区域最大宽度 |
| onModifiedChange | (isModified: boolean) => void | - | 修改状态变化回调 |
开发指南
本地开发
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 类型检查
pnpm typecheck
# 代码检查
pnpm lint
# 构建
pnpm build项目结构
src/
├── components/ # UI 组件
│ ├── editor/ # 编辑器核心
│ └── ui/ # 通用 UI
├── hooks/ # React Hooks
├── lib/ # 工具库
│ ├── word-count.ts # 字数统计
│ ├── find-replace.ts # 查找替换
│ ├── toc.ts # 目录提取
│ ├── outline.ts # 大纲获取
│ ├── export.ts # 导出功能
│ └── constants.ts # 常量定义
├── pages/ # 示例页面
│ ├── Editor.tsx # 完整编辑器
│ └── examples/ # 功能示例
└── index.ts # SDK 入口发布到 npm
前置条件
- 已安装 Node.js 18+ 和 pnpm
- 拥有 npm 账号并具有
kc-plate-editor包的发布权限 - npm 账号已开启 2FA 时,需准备好身份验证器 App(如 Google Authenticator)
发布步骤
# 1. 进入项目目录
cd kc-plate-editor
# 2. 更新版本号(选择 patch/minor/major)
npm version patch # 0.6.1 → 0.6.2
npm version minor # 0.6.1 → 0.7.0
npm version major # 0.6.1 → 1.0.0
# 或直接编辑 package.json 中的 version 字段
# 3. 验证代码质量(typecheck + lint + test)
pnpm run typecheck
pnpm run lint
pnpm run test
# 4. 构建产物
pnpm run build
# 5. 登录 npm 官方 registry
npm login --registry https://registry.npmjs.org/
# 按提示在浏览器完成登录,回到终端按 Enter 确认
# 6. 发布(开启 2FA 的账号需要 --otp 参数)
npm publish --registry https://registry.npmjs.org/ --otp=你的6位验证码
# 从身份验证器 App 获取 OTP 后立即执行(30秒有效期)发布到私有 registry
如果使用 Verdaccio、Nexus 等私有 npm 仓库:
# 1. 登录私有 registry
npm login --registry http://your-private-registry:4873/
# 2. 构建
pnpm run build
# 3. 发布到私有 registry(私服通常不需要 OTP)
npm publish --registry http://your-private-registry:4873/也可以在 .npmrc 中配置默认 registry 避免每次指定:
# .npmrc
registry=http://your-private-registry:4873/发布后验证
# 查看已发布的版本
npm view kc-plate-editor versions --registry https://registry.npmjs.org/
# 在其他项目中安装最新版
pnpm add kc-plate-editor@latest常见问题
| 错误 | 原因 | 解决方式 |
|------|------|----------|
| E403 Two-factor authentication required | npm 账号开启了 2FA | 添加 --otp=123456 参数 |
| E401 Unauthorized | 未登录或 token 过期 | 重新执行 npm login |
| E403 Forbidden | 无发布权限 | 检查 npm 账号对该包的权限 |
| EPUBLISHCONFLICT | 版本号已存在 | 更新 package.json 中的版本号 |
更多资源
- 在线示例 - 运行
pnpm dev后访问 http://localhost:3001 - 详细文档 - 查看
docs/目录 - 变更日志 - 查看
CHANGELOG.md - Plate.js 文档 - https://platejs.org
技术栈
- React 19
- Plate.js 52
- TypeScript 5.9
- Tailwind CSS 4
- Vite 7
注意: 本项目为私有项目,仅供内部使用,未经授权不得分发。
