♏ 一款浏览器端的 Markdown 编辑器,支持所见即所得(富文本)、即时渲染(类似 Typora)和分屏预览模式。An In-browser Markdown editor, support WYSIWYG (Rich Text), Instant Rendering (Typora-like) and Split View modes.
MIT License
Vditor Markdown Typora TypeScript JavaScript VueReactAngular Svelte
Vditor B3log B3log
Markdown Markdown Markdown
Markdown
Typora
Markdown
Markdown Markdown
Markdown Markdown
**** Markdown
Vditor Markdown
** Markdown Markdown
** Typora Markdown
** Markdown
npm install vditor --save
import Vditor from 'vditor'
import "~vditor/src/assets/less/index"
const vditor = new Vditor(id, {options...})
<!-- https://unpkg.com/[email protected]/dist... -->
<link rel="stylesheet" href="https://unpkg.com/vditor/dist/index.css" />
<script src="https://unpkg.com/vditor/dist/index.min.js"></script>
classicdark 2
options.theme
setTheme
Markdown HTML ant-design, lightdarkwechat 4
class="vditor-reset"
options.preview.theme
IPreviewOptions.theme
setTheme
setContentTheme
github 36
options.preview.hljs
IPreviewOptions.hljs
setTheme
setCodeTheme
id
HTMLElement
HTMLElement
options.cache.id
options.cache.enable
false
i18n | ITips | - |
undoDelay | - | |
after | - | |
height | 'auto' | |
minHeight | - | |
width | % | 'auto' |
placeholder | '' | |
lang | en_US, fr_FR, pt_BR, ja_JP, ko_KR, ru_RU, sv_SE, zh_CN, zh_TW | 'zh_CN' |
input(value: string) | - | |
focus(value: string) | - | |
blur(value: string) | - | |
keydown(event: KeyboardEvent) | - | |
esc(value: string) | esc | - |
ctrlEnter(value: string) | /ctrl+enter | - |
select(value: string) | - | |
tab |
tab \t
|
- |
typewriterMode | false | |
cdn | CDN | https://unpkg.com/vditor@${VDITOR_VERSION} |
mode | sv, ir, wysiwyg | 'ir' |
debugger | false | |
value | '' | |
theme | classic, dark | 'classic' |
icon | ant, material | 'ant' |
customRenders: {language: string, render: (element: HTMLElement, vditor: IVditor) => void}[] | [] |
toolbar: ['emoji', 'br', 'bold', '|', 'line']
src/ts/util/Options.ts
emoji
, headings
, bold
, italic
, strike
, |
, line
, quote
, list
, ordered-list
, check
,outdent
,indent
, code
, inline-code
, insert-after
, insert-before
,undo
, redo
, upload
, link
, table
, record
, edit-mode
, both
, preview
, fullscreen
, outline
, code-theme
, content-theme
, export
, devtools
, info
, help
, br
name
new Vditor('vditor', {
toolbar: [
{
hotkey: 'S',
name: 'sponsor',
tipPosition: 's',
tip: '',
className: 'right',
icon: '<svg t="1589994565028" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2808" width="32" height="32"><path d="M506.6 423.6m-29.8 0a29.8 29.8 0 1 0 59.6 0 29.8 29.8 0 1 0-59.6 0Z" fill="#0F0F0F" p-id="2809"></path><path d="M717.8 114.5c-83.5 0-158.4 65.4-211.2 122-52.7-56.6-127.7-122-211.2-122-159.5 0-273.9 129.3-273.9 288.9C21.5 562.9 429.3 913 506.6 913s485.1-350.1 485.1-509.7c0.1-159.5-114.4-288.8-273.9-288.8z" fill="#FAFCFB" p-id="2810"></path><path d="M506.6 926c-22 0-61-20.1-116-59.6-51.5-37-109.9-86.4-164.6-139-65.4-63-217.5-220.6-217.5-324 0-81.4 28.6-157.1 80.6-213.1 53.2-57.2 126.4-88.8 206.3-88.8 40 0 81.8 14.1 124.2 41.9 28.1 18.4 56.6 42.8 86.9 74.2 30.3-31.5 58.9-55.8 86.9-74.2 42.5-27.8 84.3-41.9 124.2-41.9 79.9 0 153.2 31.5 206.3 88.8 52 56 80.6 131.7 80.6 213.1 0 103.4-152.1 261-217.5 324-54.6 52.6-113.1 102-164.6 139-54.8 39.5-93.8 59.6-115.8 59.6zM295.4 127.5c-72.6 0-139.1 28.6-187.3 80.4-47.5 51.2-73.7 120.6-73.7 195.4 0 64.8 78.3 178.9 209.6 305.3 53.8 51.8 111.2 100.3 161.7 136.6 56.1 40.4 88.9 54.8 100.9 54.8s44.7-14.4 100.9-54.8c50.5-36.3 108-84.9 161.7-136.6 131.2-126.4 209.6-240.5 209.6-305.3 0-74.9-26.2-144.2-73.7-195.4-48.2-51.9-114.7-80.4-187.3-80.4-61.8 0-127.8 38.5-201.7 117.9-2.5 2.6-5.9 4.1-9.5 4.1s-7.1-1.5-9.5-4.1C423.2 166 357.2 127.5 295.4 127.5z" fill="#141414" p-id="2811"></path><path d="M353.9 415.6m-33.8 0a33.8 33.8 0 1 0 67.6 0 33.8 33.8 0 1 0-67.6 0Z" fill="#0F0F0F" p-id="2812"></path><path d="M659.3 415.6m-33.8 0a33.8 33.8 0 1 0 67.6 0 33.8 33.8 0 1 0-67.6 0Z" fill="#0F0F0F" p-id="2813"></path><path d="M411.6 538.5c0 52.3 42.8 95 95 95 52.3 0 95-42.8 95-95v-31.7h-190v31.7z" fill="#5B5143" p-id="2814"></path><path d="M506.6 646.5c-59.6 0-108-48.5-108-108v-31.7c0-7.2 5.8-13 13-13h190.1c7.2 0 13 5.8 13 13v31.7c0 59.5-48.5 108-108.1 108z m-82-126.7v18.7c0 45.2 36.8 82 82 82s82-36.8 82-82v-18.7h-164z" fill="#141414" p-id="2815"></path><path d="M450.4 578.9a54.7 27.5 0 1 0 109.4 0 54.7 27.5 0 1 0-109.4 0Z" fill="#EA64F9" p-id="2816"></path><path d="M256 502.7a32.1 27.5 0 1 0 64.2 0 32.1 27.5 0 1 0-64.2 0Z" fill="#EFAFF9" p-id="2817"></path><path d="M703.3 502.7a32.1 27.5 0 1 0 64.2 0 32.1 27.5 0 1 0-64.2 0Z" fill="#EFAFF9" p-id="2818"></path></svg>',
click () {alert('https://ld246.com/sponsor')},
}],
})
name | - | |
icon | svg | - |
tip | - | |
tipPosition | 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'e' | - |
hotkey | // | - |
suffix | - | |
prefix | - | |
click(event: Event, vditor: IVditor) | - | |
className | '' | |
toolbar?: Array<options.toolbar> | - |
hide | false | |
pin | false |
enable | false | |
after(length: number, counter: options.counter): void | - | |
max | - | |
type | 'markdown', 'text' | 'markdown' |
enable | localStorage | true |
id | key**** | - |
after(html: string): string | - |
wysiwyg
enable | false | |
add(id: string, text: string, commentsData: ICommentsData[]) | - | |
remove(ids: string[]) | - | |
scroll(top: number) | - | |
adjustTop(commentsData: ICommentsData[]) | - |
delay | debounce | 1000 |
maxWidth | 800 | |
mode | both, editor | 'both' |
url | md | - |
parse(element: HTMLElement) | - | |
transform(html: string): string | - |
defaultLang | '' | |
enable | true | |
style | Chroma | github |
lineNumber | false | |
langs | CODE_LANGUAGES | |
renderMenu(code: HTMLElement, copy: HTMLElement) | - |
autoSpace | false | |
gfmAutoLink | true | |
fixTermTypo | false | |
toc | false | |
footnotes | true | |
codeBlockPreview | wysiwyg ir | true |
mathBlockPreview | wysiwyg ir | true |
paragraphBeginningSpace | false | |
sanitize | XSS | true |
listStyle | data-style | false |
linkBase | '' | |
linkPrefix | '' | |
mark | mark | false |
current | "light" | |
list | { "ant-design": "Ant Design", dark: "Dark", light: "Light", wechat: "WeChat" } | |
path | https://unpkg.com/vditor@${VDITOR_VERSION}/dist/css/content-theme |
inlineDigit | $ | false |
macros | MathJax | {} |
engine | KaTeX, MathJax | 'KaTeX' |
mathJaxOptions | MathJax | - |
["desktop", "tablet", "mobile", "mp-wechat", "zhihu"]
key | - | |
text | - | |
tooltip | - | |
className | - | |
click(key: string) | - |
enable | true |
isPreview | true | |
preview(bom: Element) => void | - |
isOpen | true | |
click(bom: Element) => void | - |
parse | md | true |
delay | debounce | 200 |
emoji | lute/emoji_map | { '+1': '', '-1': '', 'heart': '', 'cold_sweat': '' } |
emojiTail | - | |
emojiPath | https://unpkg.com/vditor@${VDITOR_VERSION}/dist/images/emoji |
|
extend: IHintExtend[] | @/ | [] |
interface IHintData {
html: string;
value: string;
}
interface IHintExtend {
key: string;
hint?(value: string): IHintData[] | Promise<IHintData[]>;
}
format
// POST data
xhr.send(formData); // formData = FormData.append("file[]", File)
// return data
{
"msg": "",
"code": 0,
"data": {
"errFiles": ['filename', 'filename2'],
"succMap": {
"filename3": "filepath3",
"filename3": "filepath3"
}
}
}
linkToImgUrl
// POST data
xhr.send(JSON.stringify({url: src})); // src
// return data
{
msg: '',
code: 0,
data : {
originalURL: '',
url: ''
}
}
success``format``error
if (xhr.status === 200) {
if (vditor.options.upload.success) {
vditor.options.upload.success(editorElement, xhr.responseText);
} else {
let responseText = xhr.responseText;
if (vditor.options.upload.format) {
responseText = vditor.options.upload.format(files as File [], xhr.responseText);
}
genUploadedLabel(responseText, vditor);
}
} else {
if (vditor.options.upload.error) {
vditor.options.upload.error(xhr.responseText);
} else {
vditor.tip.show(xhr.responseText);
}
}
url | url | '' |
max | Byte | 10 * 1024 * 1024 |
linkToImgUrl | url | '' |
linkToImgCallback(responseText: string) | - | |
linkToImgFormat(responseText: string): string | - | |
success(editor: HTMLPreElement, msg: string) | - | |
error(msg: string) | - | |
token | CORS X-Upload-Token | - |
withCredentials | false | |
headers | - | |
filename(name: string): string | name => name.replace(/\W/g, '') | |
accept | input accept | - |
validate(files: File[]) => string | boolean | true | - |
handler(files: File[]) => string | null | Promise | Promise | - | |
format(files: File[], responseText: string): string | - | |
file(files: File[]): File[] | Promise<File[]> | - | |
setHeaders(): { [key: string]: string } | - | |
extraData: { [key: string]: string | Blob } | FormData | - |
multiple | true | |
fieldName | 'file[]' | |
renderLinkDest?(vditor: IVditor, node: ILuteNode, entering: boolean): [string, number] | '' |
enable | false | |
position | 'top', 'bottom' | 'bottom' |
after(height: number) | - |
preview | className | '' |
index | 90 |
enable | false | |
position | 'left', 'right' | 'left' |
exportJSON(markdown: string) | Markdown JSON |
getValue() | Markdown |
getHTML() | HTML |
insertValue(value: string, render = true) | Markdown |
focus() | |
blur() | |
disabled() | |
enable() | |
getSelection(): string | |
setValue(markdown: string, clearStack = false) | |
clearStack() | |
renderPreview(value?: string) | |
getCursorPosition():{top: number, left: number} | |
deleteValue() | |
updateValue(value: string) | |
isUploading() | |
clearCache() | |
disabledCache() | |
enableCache() | |
html2md(value: string) | HTML md |
tip(text: string, time: number) | time 0 |
setPreviewMode(mode: "both" | "editor") | |
setTheme(theme: "dark" | "classic", contentTheme?: string, codeTheme?: string, contentThemePath?: string) | |
getCurrentMode(): string | |
destroy() | |
getCommentIds(): {id: string, top: number}[] | |
hlCommentIds(ids: string[]) | |
unHlCommentIds(ids: string[]) | |
removeCommentIds(removeIds: string[]) | |
updateToolbarConfig(config: {hide?: boolean, pin?: boolean}) |
Vditor.mermaidRender(document)
import VditorPreview from 'vditor/dist/method.min'
VditorPreview.mermaidRender(document)
preview
previewElement: HTMLDivElement, //
markdown: string, // markdown
options?: IPreviewOptions {
mode: "dark" | "light";
anchor?: number; // 012 0
customEmoji?: { [key: string]: string }; // emoji {}
lang?: (keyof II18nLang); // 'zh_CN'
emojiPath?: string; //
hljs?: IHljs; // options.preview.hljs
speech?: { //
enable?: boolean,
};
math?: IMath; //
cdn?: string; // CDN
transform?(html: string): string; //
after?(); //
lazyLoadImage?: string; // Loading
markdown?: options.preview.markdown;
theme?: options.preview.theme;
render?: options.preview.render;
renderers?: ILuteRender; // https://ld246.com/article/1588412297062
}
method.min.js
index.min.js
npm install
npm run start
http://localhost:9000
npm run build
distCDN
options
IPreviewOptions
cdn``emojiPath
, themes
highlightRender
, mathRender
, abcRender
, chartRender
, mermaidRender
flowchartRender``mindmapRender``graphvizRender``setCodeTheme``setContentTheme
cdn**** CHANGELOG ****
Vditor MIT
Sym WYSIWYG HTML
Markdown md CodeMirror
CodeMirror @ Emoji
Sym Sym V TypeScript ts md
Vditor