vditor

♏ 一款浏览器端的 Markdown 编辑器,支持所见即所得(富文本)、即时渲染(类似 Typora)和分屏预览模式。An In-browser Markdown editor, support WYSIWYG (Rich Text), Instant Rendering (Typora-like) and Split View modes.

MIT License

Downloads
105.5K
Stars
7.8K
Committers
54

Vditor Markdown Typora TypeScript JavaScript VueReactAngular Svelte

Vditor B3log B3log

Markdown Markdown Markdown

  • Markdown

  • Typora

  • Markdown

  • Markdown Markdown

  • Markdown Markdown

**** Markdown

  • Markdown
  • Markdown

Vditor Markdown

  • wysiwygirsv
  • graphviz plantumlUML
  • /
  • CommonMark GFM Markdown 10+
  • 36+
  • /at/
  • CORS
  • HTML Markdown

WYSIWYG

** Markdown Markdown

IR

** Typora Markdown

SV

** Markdown

  • CommonMark ATX Setext HTML HTML HTML
  • GFM XSS
  • Markdown ToC ID
    • Mermaid
    • Graphviz
    • ECharts
  • abc.js
  • MathJax KaTeX
  • YAML Front Matter

CommonJS

npm install vditor --save
import Vditor from 'vditor'
import "~vditor/src/assets/less/index"

const vditor = new Vditor(id, {options...})

HTML script

  • HTML CSS JavaScript demo
<!--  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

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

API

id

id HTMLElement

HTMLElement options.cache.id options.cache.enable false

options

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}[] []

options.toolbar

  • name toolbar: ['emoji', 'br', 'bold', '|', 'line'] src/ts/util/Options.ts
  • name 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> -

options.toolbarConfig

hide false
pin false

options.counter

enable false
after(length: number, counter: options.counter): void -
max -
type 'markdown', 'text' 'markdown'

options.cache

enable localStorage true
id key**** -
after(html: string): string -

options.comment

wysiwyg

enable false
add(id: string, text: string, commentsData: ICommentsData[]) -
remove(ids: string[]) -
scroll(top: number) -
adjustTop(commentsData: ICommentsData[]) -

options.preview

delay debounce 1000
maxWidth 800
mode both, editor 'both'
url md -
parse(element: HTMLElement) -
transform(html: string): string -

options.preview.hljs

defaultLang ''
enable true
style Chroma github
lineNumber false
langs CODE_LANGUAGES
renderMenu(code: HTMLElement, copy: HTMLElement) -

options.preview.markdown

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

options.preview.theme

current "light"
list { "ant-design": "Ant Design", dark: "Dark", light: "Light", wechat: "WeChat" }
path https://unpkg.com/vditor@${VDITOR_VERSION}/dist/css/content-theme

options.preview.math

inlineDigit $ false
macros MathJax {}
engine KaTeX, MathJax 'KaTeX'
mathJaxOptions MathJax -

options.preview.actions?: Array<IPreviewAction | IPreviewActionCustom>

["desktop", "tablet", "mobile", "mp-wechat", "zhihu"]

key -
text -
tooltip -
className -
click(key: string) -

options.preview.render.media

enable true

options.image

isPreview true
preview(bom: Element) => void -

options.link

isOpen true
click(bom: Element) => void -

options.hint

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[]>;
}

options.upload

  • 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] ''

options.resize

enable false
position 'top', 'bottom' 'bottom'
after(height: number) -

options.classes

preview className ''

options.fullscreen

index 90

options.outline

enable false
position 'left', 'right' 'left'

methods

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})

static methods

Vditor.mermaidRender(document)
import VditorPreview from 'vditor/dist/method.min'
VditorPreview.mermaidRender(document)
  • Markdown 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
previewImage(oldImgElement: HTMLImageElement, lang: keyof II18n = "zh_CN", theme = "classic")
mermaidRender(element: HTMLElement, cdn = options.cdn, theme = options.theme) //
flowchartRender(element: HTMLElement, cdn = options.cdn) flowchart
codeRender(element: HTMLElement, option?: IHljs) element
chartRender(element: (HTMLElement | Document) = document, cdn = options.cdn, theme = options.theme)
mindmapRender(element: (HTMLElement | Document) = document, cdn = options.cdn, theme = options.theme)
plantumlRender(element: (HTMLElement | Document) = document, cdn = options.cdn) plantuml
abcRender(element: (HTMLElement | Document) = document, cdn = options.cdn)
md2html(mdText: string, options?: IPreviewOptions): Promise<string> Markdown HTML
preview(previewElement: HTMLDivElement, markdown: string, options?: IPreviewOptions) Markdown
highlightRender(hljsOption?: IHljs, element?: HTMLElement | Document, cdn = options.cdn) element
mediaRender(element: HTMLElement) iframe
mathRender(element: HTMLElement, options?: {cdn?: string, math?: IMath})
speechRender(element: HTMLElement, lang?: (keyof II18nLang))
graphvizRender(element: HTMLElement, cdn?: string) graphviz
outlineRender(contentElement: HTMLElement, targetElement: Element)
lazyLoadImageRender(element: (HTMLElement | Document) = document)
setCodeTheme(codeTheme: string, cdn = options.cdn) codeTheme options.preview.hljs.style
setContentTheme(contentTheme: string, path: string) contentTheme options.preview.theme.list

  1. node LTS
  2. npm install
  3. npm run start http://localhost:9000
  4. npm run build dist

CDN

CDN https://unpkg.com/vditor@

CDN

  • options IPreviewOptions cdn``emojiPath, themes
  • highlightRender , mathRender , abcRender , chartRender , mermaidRender flowchartRender``mindmapRender``graphvizRender``setCodeTheme``setContentTheme cdn
  • build dist jsDelivr dist

**** CHANGELOG ****

Markdown

Vditor MIT

  • Lute Markdown Go JavaScript
  • highlight.jsJavaScript syntax highlighter
  • mermaidGeneration of diagram and flowchart from text in a similar manner as Markdown
  • incubator-echartsA powerful, interactive charting and visualization library for browser
  • abcjsJavaScript library for rendering standard music notation in a browser

Sym WYSIWYG HTML

Markdown md CodeMirror

CodeMirror @ Emoji

Sym Sym V TypeScript ts md

Vditor

Package Rankings
Top 1.04% on Npmjs.org
Top 22.52% on Repo1.maven.org
Top 3.8% on Proxy.golang.org
Related Projects