npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kc-plate-editor

v0.6.1

Published

A powerful and customizable rich text editor built on Plate

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 更新内容

  • 依赖全面升级 - 更新至最新稳定版本
    • ai 5.0.28 → 6.0.45(重大升级)
    • @ai-sdk/react 2.0.28 → 3.0.47(重大升级)
    • vite 5.4.11 → 7.3.1(重大升级)
    • platejs 52.0.1 → 52.0.17
    • @platejs/ai 52.0.6 → 52.1.0
    • @platejs/markdown 52.0.4 → 52.1.0
    • typescript 5.9.2 → 5.9.3
    • tailwindcss 4.1.13 → 4.1.18
    • zod 4.1.12 → 4.3.5
    • zustand 5.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 函数 - 支持 customCopilotcustomCommand 自定义 AI 逻辑
  • 文件验证 - 支持 maxSizeacceptTypes 文件验证
  • 额外表单数据 - 支持 extraData 添加额外表单字段
  • 灵活响应解析 - 支持多种服务端响应格式
  • 配置辅助函数 - 新增 getUploadConfiggetAiConfig 等辅助函数

v0.5.5 更新内容

  • 快捷键保存 - 支持 Ctrl+S / Cmd+S 快捷键保存
  • 本地存储 - 新增 saveToLocalStorage()clearLocalStorage() 方法
  • 目录跳转优化 - 修复目录点击跳转不准确的问题
  • onSave 回调 - 新增 onSave 属性用于处理快捷键保存事件

v0.5.4 更新内容

  • 修复编辑器容器双滚动条问题
  • 优化 EditorContainerdemo 变体高度设置
  • 修复工具栏吸顶在嵌套滚动容器中失效的问题

v0.5.0 更新内容

  • 优化字数统计、查找替换、目录功能,使用官方 Plate.js API
  • 新增公共工具函数 text-utils.ts
  • 统一常量管理(HEADING_DEPTHFONT_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

注意: 本项目为私有项目,仅供内部使用,未经授权不得分发。