公众号版式
默认 677px 正文版心、17px / 1.75 行距,窄屏自动收窄。宽屏采用 fluid 版心随容器生长,两侧留白恒定为 paperGap,不会留出一大块空白。
677px 正文版心、17px/1.75 行距,与公众号观感一致。图片与视频上传(整包 + 分片)、 20 种语言代码块与语法高亮、Markdown 一键导入、HTML 源码模式 —— 一个 <script> 标签或一行 Vue 组件就能用上。
不想构建?直接引用 CDN:<script src="https://unpkg.com/kyle-editor"></script>
从版心、字号到上传、源码、Markdown,全部内置;也可以按需裁剪,只要核心体积不到 200 KB。
默认 677px 正文版心、17px / 1.75 行距,窄屏自动收窄。宽屏采用 fluid 版心随容器生长,两侧留白恒定为 paperGap,不会留出一大块空白。
普通上传与分片上传各自独立配置。分片只打一个接口,响应里 url 非空即成功,天然支持秒传;并发、重试、取消、断点续传都在。
20 种语言可选,支持按别名搜索(js / mysql / yml)。光标落在块内可边写边高亮;产物是干净代码,重新导入会自动重新着色。
选文件、拖文件或直接粘文本,实时预览转换结果与结构统计,再选落点(替换全文 / 追加文末 / 插入光标)。把 .md 拖进正文也能转。
工具栏常驻「源码」按钮,⌘/Ctrl+Shift+K 直达。格式化缩进、一键复制、直接编辑并应用(回填自动净化),方便对接第三方模板。
点击图片浮出气泡工具条(对齐 / 宽度 / 圆角 / 替换 / 删除),右下角拖拽把手可按百分比实时缩放,双击看原图,大图上传前自动压缩。
用 customButtons 加下拉选择按钮:数据可静态配置或从接口读取,选中后插入跳转链接或直接打开。面板自带搜索、分组、加载与重试。
标签 / 属性双白名单净化,拦截 on*、javascript:、script/iframe。纯前端运行,样式随 JS 注入,不用额外引 CSS,也不上传任何内容到远端。
不配 height 时内容区随内容撑开、整页滚动,工具栏尽量吸附视口顶部;配上就固定高度并内部滚动。实时字数 / 图片 / 视频统计开箱可用。
支持 <script> 直接引入、ESM / 打包器,以及作为 Vue 3 组件安装。三种方式 API 完全一致。
<div id="editor"></div>
<script src="https://unpkg.com/kyle-editor"></script>
<script>
const editor = KEditor.create('#editor', {
placeholder: '开始撰写内容,支持拖拽 / 粘贴图片与视频…',
primaryColor: '#07c160',
// 高度默认随内容自动撑开;要固定高度 + 内部滚动就写 height: 460
})
editor.on('change', ({ html }) => console.log(html))
</script>
unpkg / jsdelivr 的默认入口就是压缩版,因此 unpkg.com/kyle-editor 与 .../dist/keditor.umd.min.js 等价。调试可换成未压缩的 keditor.umd.js,全局变量与 API 完全一致。
npm i kyle-editor
import KEditor from 'kyle-editor'
const editor = KEditor.create('#editor') // 高度默认自适应内容
// 需要时按需取纯函数,不依赖实例
import { sanitizeHtml, markdownToHtml, highlightCode } from 'kyle-editor'
包内自带 TypeScript 声明,import KEditor from 'kyle-editor' 即有完整类型提示,无需额外安装 @types。
// main.js
import { createApp } from 'vue'
import KEditorVue from 'kyle-editor/vue'
createApp(App).use(KEditorVue).mount('#app')
<template>
<k-editor v-model="form.content" :options="options" :disabled="saving" @upload="onUpload" />
</template>
<script setup>
import { ref } from 'vue'
const form = ref({ content: '' })
const options = {
height: 420, // 不写 = 自适应内容高度
uploader: '/api/upload', // 普通上传:整个文件一次请求
chunkUploader: '/api/chunk-upload', // 分片上传:大文件自动切片(≥ 5MB)
upload: { mode: 'auto', chunkSize: 5 * 1024 * 1024, concurrency: 4 },
}
const onUpload = (payload) => console.log(payload.stage, payload.percent)
</script>
kyle-editor/vue 的默认导出就是组件本身(自带 install),所以 app.use() 与直接当组件用两种写法等价;只想显式拿插件时用 import { KEditorPlugin }。
小图走普通上传(整个文件一次请求),大视频走分片上传(切片并发 + 重试)。 两条链路互不影响,可以只配一条,也可以都配;配了一条就不会被默认的 mock 静默顶上, 避免真实后端只接了一半却产生 blob: 假地址。
chunkUploader,某个响应里 url 非空即代表整个文件成功,剩余分片不再上传 —— 天然支持秒传。
upload.mode 默认 'auto',达到 chunkThreshold(默认 5MB)才走分片。
'mock' 适配器生成 blob: 地址,改一行配置即接真实接口。
KEditor.create('#editor', {
uploader: '/api/upload', // 普通上传(整包)
chunkUploader: '/api/chunk-upload', // 分片上传(切片并发)
upload: {
mode: 'auto', // auto | normal | chunk
chunkThreshold: 5 * 1024 * 1024, // 达到该体积才分片
chunkSize: 1024 * 1024, // 分片大小
concurrency: 4, // 并发分片数
retries: 2, // 单片失败重试
},
})
// 需要鉴权 / 自定义字段时(两条链路写法相同)
chunkUploader: {
type: 'http',
url: '/api/chunk-upload',
fieldName: 'chunk', // 默认 'file'
headers: () => ({ Authorization: `Bearer ${getToken()}` }),
parse: (json) => json.data, // 默认 json.data ?? json
pickUrl: (json) => json?.payload?.filePath || '', // 默认认 url / fileUrl / src
}
尾栏「导入MD」按钮打开导入面板:选文件 / 拖文件 / 粘文本,实时预览转换结果与结构统计,
再选落点导入。把 .md、.markdown、.txt 文件拖进正文也能直接转换。
data-k-lang + class="language-x",直接接入高亮。
<script>、on*、javascript: 会被清掉或转义;编码自动兜底 GBK / GB18030 / Big5。
KEditor.create('#editor', {
markdown: {
insertMode: 'replace', // replace | append | cursor
maxSize: 5 * 1024 * 1024, // 单文件上限,0 = 不限
gfm: true, // 表格 / 删除线 / 任务列表 / 裸链接
breaks: false, // 段落内单换行是否转 <br>
linkTarget: false, // 外链自动加 target + rel
frontMatter: true, // 丢弃开头 YAML front matter
},
})
// source 支持 文本 / File / { name, content }
await editor.importMarkdown(mdText)
await editor.importMarkdown(file, { mode: 'append' })
await editor.setMarkdown(mdText) // 替换全文
await editor.appendMarkdown(mdText) // 追加文末
editor.on('markdownimport', ({ mode, name, summary }) => {
console.log(mode, name, summary)
})
KEditor.markdownToHtml('# 标题\n\n正文')
// => '<h1>标题</h1>\n<p>正文</p>'
KEditor.markdownSummary(html)
// => { headings, paragraphs, lists, items,
// codeBlocks, quotes, tables, images, links }
点工具栏「代码块」就是语言面板,支持按名称或别名搜索。光标已经在某个代码块里时, 同一个面板变成「代码块语言」,选一项即改写当前块并重新着色。
高亮器是内置的极简 tokenizer(零依赖),正则带长度保护:单块超过 60 KB 按纯文本处理,避免长文卡顿。
别名同样可用:js ts py golang yml mysql c++ md
KEditor.create('#editor', {
code: {
languages: ['js', 'python', 'go', 'sql'], // 只留几种;不写 = 全部
defaultLanguage: 'python',
highlight: true, // false 只保留纯文本,语言标记照写
},
})
editor.insertCode('const a = 1', 'js')
editor.setCodeLanguage('sql') // 改写光标所在块
editor.getCodeLanguage() // 不在块内返回 ''
editor.codeLanguages() // [{ value, label, hint }, …]
editor.getState().inCode // 光标是否在代码块内
// 纯函数:源码进、带 token 的 HTML 片段出(已转义)
KEditor.highlightCode(code, 'python')
KEditor.normalizeLang('MySQL') // => 'sql'
<pre data-k-lang="javascript"><code class="language-javascript">
const editor = KEditor.create('#editor')
</code></pre>
// getHTML() / 源码模式 / 复制出去的内容都不含
// token <span>,重新导入会自动重新高亮
工具栏始终单行显示,放不下时可左右滑动,并有常显滚动条、边缘渐隐与首次引导气泡保证可发现性;「导入MD / 源码 / 全屏」固定吸附右端。
| 按钮 | 面板内容 |
|---|---|
heading | 正文 / 标题一 / 标题二 / 标题三,每档给出真实字号预览 + 用途说明,按钮文案跟随当前段落 |
fontsize | 12–32px 网格,当前字号高亮 |
foreColor / backColor | 最近使用 + 主题色 + 标准色,色块带中文名称;底部取色器 + HEX 输入 + 应用,右上角「恢复默认」 |
image / video | 两个页签:本地上传(点击或拖拽到虚线区)/ 网络链接(粘贴地址即插入,带可访问性校验) |
table | 10×8 网格选择,实时显示「N 列 × M 行」 |
link | 文字 / 地址 / 新窗口打开;已链接时提供「移除链接」 |
code | 「插入代码块 / 代码块语言」面板,20 种语言含一句话说明,支持搜索与回车 |
markdown | 「导入MD」:选择文件 / 粘贴文本 + 三种落点 + 实时预览与结构统计 |
| 快捷键 | 作用 |
|---|---|
⌘/Ctrl + B / I / U | 加粗 / 斜体 / 下划线 |
⌘/Ctrl + K | 插入链接 |
⌘/Ctrl + Shift + K | 切换 HTML 源码模式 |
⌘/Ctrl + Z / Shift + Z / Y | 撤销 / 重做(保留光标位置) |
⌘/Ctrl + S | 触发 save 事件 |
Esc | 关闭面板 / 退出预览 |