Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
md-editor-v3
Advanced tools
Markdown editor for vue3, developed in jsx and typescript, dark theme、beautify content by prettier、render articles directly、paste or clip the picture and upload it...
English | 中文
Markdown editor for vue3, developed in jsx
and typescript
.
Documentation and example: Go
Use it online: Go
The same series editor for react: md-editor-rt
prettier
(only for markdown content, not the code and other text).default
, vuepress
, github
, cyanosis
, mk-cute
, smart-blue
styles(not identical). It can be customized also(Refer to example page).mermaid
(>=1.8.0), katex
mathematical formula(>=1.9.0).yarn add md-editor-v3
Use existing extension of language and theme, such as Japanese
yarn add @vavt/cm-extension
Use existing components of toolbar, such as exporting content as PDF
yarn add @vavt/v3-extension
For more ways to use or contribute, please refer to: md-editor-extension
<template>
<MdEditor v-model="text" />
</template>
<script setup>
import { ref } from 'vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const text = ref('# Hello Editor');
</script>
^4.0.0
, internal components can be imported on-demand.
If there are multiple editors on the page, please set different
editorId
for each editor!
<template>
<MdPreview :editorId="id" :modelValue="text" />
<MdCatalog :editorId="id" :scrollElement="scrollElement" />
</template>
<script setup>
import { ref } from 'vue';
import { MdPreview, MdCatalog } from 'md-editor-v3';
import 'md-editor-v3/lib/preview.css';
const id = 'preview-only';
const text = ref('# Hello Editor');
const scrollElement = document.documentElement;
</script>
Default theme | Dark theme | Preview only |
---|---|---|
Inputing prompt and mark, emoji extensions
name | type | default | description |
---|---|---|---|
modelValue | string | '' | Markdown content, use v-model in vue template |
theme | 'light' | 'dark' | 'light' | Editor theme |
class | string | '' | |
language | string | 'zh-CN' | Build-in language('zh-CN','en-US') |
editorId | string | 'md-editor-v3' | Editor's id, it is used when there are more than two editors in the same page |
showCodeRowNumber | boolean | false | Show row number for code block or not |
previewTheme | 'default' | 'github' | 'vuepress' | 'mk-cute' | 'smart-blue' | 'cyanosis' | 'default' | Theme of preview, can be customized |
style | string | CSSProperties | {} | Inline style |
noMermaid | boolean | false | Use mermaid or not |
noKatex | boolean | false | Use katex or not |
codeTheme | 'atom' | 'a11y' | 'github' | 'gradient' | 'kimbie' | 'paraiso' | 'qtcreator' | 'stackoverflow' | 'atom' | Highlight code style, can be customized also |
mdHeadingId | (text: string, level: number, index: number) => string | (text) => text | H1-H6 ID generator |
sanitize | (html: string) => string | (html) => html | Sanitize the html, prevent XSS. After 3.x, dangerous code has been processed by default. Please do not use this attribute unless there are special requirements |
noIconfont | boolean | false | Not append iconfont script, download different versions SVG或Font Class and import it by yourself |
formatCopiedText | (text: string) => string | (text: string) => text | Format copied code |
codeStyleReverse | boolean | true | Code style will be reversed to dark while code block of the theme has a dark background |
codeStyleReverseList | Array<string> | ['default', 'mk-cute'] | Themes to be reversed |
noHighlight | boolean | false | Highlight code or not |
noImgZoomIn | boolean | false | Enable the function of enlarging images |
customIcon | CustomIcon | {} | Customized icons |
sanitizeMermaid | (h: string) => Promise<string> | (h: string) => Promise.resolve(h) | Convert the generated mermaid code |
codeFoldable | boolean | true | Whether to enable code folding feature |
autoFoldThreshold | number | 30 | Threshold for triggering automatic code folding by line count |
Except for the same as MdPreview
:
name | type | default | description |
---|---|---|---|
pageFullscreen | boolean | false | Screenfull in web page |
preview | boolean | true | Preview content in editor |
htmlPreview | boolean | false | Preview html in editor(If true, preview must be false) |
toolbars | Array<ToolbarNames | number> | [toolbars] | Show contents of toolbar, all keyssee toolbars below |
toolbarsExclude | Array<ToolbarNames | number> | [] | Don't show contents of toolbar, all keystoolbars |
noPrettier | boolean | false | Use prettier to beautify content or not |
tabWidth | number | 2 | One tab eq some spaces |
tableShape | [number, number] | [number, number, number, number] | [6, 4] | Preset the size of the table, [columns, rows, Maximum number of columns, Maximum number of rows] |
placeholder | string | '' | |
footers | Array<'markdownTotal' | '=' | 'scrollSwitch' | number> | ['markdownTotal', '=', 'scrollSwitch'] | Show contents of footer, they are divided by '=' . Set it to [] to hidden footer |
scrollAuto | boolean | true | Scroll default setting |
noUploadImg | boolean | false | Not show the entrance to upload pictures |
autoFocus | boolean | false | same as autofocus in native textarea |
disabled | boolean | false | same as disabled in native textarea |
readOnly | boolean | false | same as readonly in native textarea |
maxLength | number | same as maxlength in native textarea | |
autoDetectCode | boolean | false | auto detect the type of pasted code, only support that copied from vscode |
completions | Array<CompletionSource> | [] | @codemirror/autocomplete List of function to match keywords |
showToolbarName | boolean | false | Show toolbar name or not |
inputBoxWitdh | string | '50%' | Default width of input box |
transformImgUrl | (imgUrl: string) => string | Promise<string> | t => t | Transform image links |
[
'bold',
'underline',
'italic',
'-',
'strikeThrough',
'title',
'sub',
'sup',
'quote',
'unorderedList',
'orderedList',
'task', // ^2.4.0
'-',
'codeRow',
'code',
'link',
'image',
'table',
'mermaid',
'katex',
'-',
'revoke',
'next',
'save',
'=',
'pageFullscreen',
'fullscreen',
'preview',
'previewOnly',
'htmlPreview',
'catalog',
'github'
];
You can sort the toolbar as you like, split tools by
'-'
, the left and right toolbars are divided by'='
!
You can customize the toolbar. To display them, put index of
defToolbars
intotoolbars
(this is not standard), for more usage, please refer to docs.
Expand language, you need to replace all the content here:
export interface ToolbarTips {
bold?: string;
underline?: string;
italic?: string;
strikeThrough?: string;
title?: string;
sub?: string;
sup?: string;
quote?: string;
unorderedList?: string;
orderedList?: string;
task?: string; // ^2.4.0
codeRow?: string;
code?: string;
link?: string;
image?: string;
table?: string;
mermaid?: string;
katex?: string;
revoke?: string;
next?: string;
save?: string;
prettier?: string;
pageFullscreen?: string;
fullscreen?: string;
catalog?: string;
preview?: string;
previewOnly?: string;
htmlPreview?: string;
github?: string;
'-'?: string;
'='?: string;
}
export interface StaticTextDefaultValue {
// Toolbar hover tips(html title)
toolbarTips?: ToolbarTips;
// H1-H6 dropdown menu item
titleItem?: {
h1?: string;
h2?: string;
h3?: string;
h4?: string;
h5?: string;
h6?: string;
};
imgTitleItem?: {
link: string;
upload: string;
clip2upload: string;
};
// The modal tips of add link or upload picture
linkModalTips?: {
linkTitle?: string;
imageTitle?: string;
descLabel?: string;
descLabelPlaceHolder?: string;
urlLabel?: string;
urlLabelPlaceHolder?: string;
buttonOK?: string;
};
// The modal tips of clip the picture, v1.2.0
clipModalTips?: {
title?: string;
buttonUpload?: string;
};
copyCode?: {
text?: string;
successTips?: string;
failTips?: string;
};
mermaid?: {
flow?: string;
sequence?: string;
gantt?: string;
class?: string;
state?: string;
pie?: string;
relationship?: string;
journey?: string;
};
katex?: {
// formula inline
inline: string;
// formula block
block: string;
};
footer?: {
markdownTotal: string;
scrollAuto: string;
};
}
name | type | description |
---|---|---|
onHtmlChanged | html: string | Compile markdown successful event, you can use it to get the html code |
onGetCatalog | list: Array<HeadList> | Get catalog of article |
Except for the same as MdPreview
:
name | type | description |
---|---|---|
onChange | value: string | Content changed(bind to oninput of textarea ) |
onSave | value: string, html: Promise<string> | Saving content, ctrl+s and clicking button will trigger it |
onUploadImg | files: Array<File>, callback: (urls: string[] | { url: string; alt: string; title: string }[]) => void | Uploading picture, when picture is uploading the modal will not close, please provide right urls to the callback function |
onError | err: { name: 'Cropper' | 'fullscreen' | 'prettier' | 'overlength'; message: string } | Catch run-time error, Cropper , fullscreen and prettier are used when they are not loaded. And content exceeds the length limit error |
onBlur | event: FocusEvent | Textarea has lost focus |
onFocus | event: FocusEvent | Textarea has received focus |
onInput | event: Event | Element gets input |
onDrop | event: DragEvent | Selection is being dragged |
onInputBoxWitdhChange | (width: string) => void | Width of input box has been changed |
name | type | default | description |
---|---|---|---|
defToolbars | Array<DropdownToolbar | NormalToolbar | ModalToolbar> | null | Custom toolbar in DropdownToolbar , NormalToolbar or ModalToolbar |
defFooters | Array<string | VNode | JSX.Element> | null | Custom footer |
NormalToolbar
example:
<template>
<MdEditor>
<template #defToolbars>
<NormalToolbar title="mark" @onClick="handler">
<template #trigger>
<svg class="md-editor-icon" aria-hidden="true">
<use xlink:href="#md-editor-icon-mark"></use>
</svg>
</template>
</NormalToolbar>
</template>
</MdEditor>
</template>
<script setup lang="ts">
import { MdEditor, NormalToolbar } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const handler = () => {
console.log('NormalToolbar clicked!');
};
</script>
After 2.5.0, Editor exposes several methods on the instance, used to get or change the internal status of the editor.
<template>
<MdEditor ref="editorRef" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { MdEditor } from 'md-editor-v3';
import type { ExposeParam } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const editorRef = ref<ExposeParam>();
onMounted(() => {
editorRef.value?.on('catalog', console.log);
editorRef.value?.toggleCatalog(true);
});
</script>
Switched to the opposite status, if toggle without input parameter.
Get the internal state of the editor, including pageFullscreen, fullscreen, preview, htmlPreview, catalog, etc.
pageFullscreen
editorRef.value?.on('pageFullscreen', (status) => console.log(status));
fullscreen
editorRef.value?.on('fullscreen', (status) => console.log(status));
preview
editorRef.value?.on('preview', (status) => console.log(status));
previewOnly
editorRef.value?.on('previewOnly', (status) => console.log(status));
htmlPreview
editorRef.value?.on('htmlPreview', (status) => console.log(status));
catalog
editorRef.value?.on('catalog', (status) => console.log(status));
Toggle status of fullscreen within the page.
editorRef.value?.togglePageFullscreen(true);
Toggle status of fullscreen widthin browser.
editorRef.value?.toggleFullscreen(true);
Toggle status of preview.
editorRef.value?.togglePreview(true);
Toggle into Preview Only Mode.
editorRef.value?.togglePreviewOnly(true);
Toggle status of htmlPreview.
editorRef.value?.toggleHtmlPreview(true);
Toggle status of catalog.
editorRef.value?.toggleCatalog(true);
editorRef.value?.triggerSave();
Manually insert content into textarea.
/**
* @params selectedText
*/
editorRef.value?.insert((selectedText) => {
/**
* @return targetValue Content to be inserted
* @return select Automatically select content, default: true
* @return deviationStart Start position of the selected content, default: 0
* @return deviationEnd End position of the selected content, default: 0
*/
return {
targetValue: `${selectedText}`,
select: true,
deviationStart: 0,
deviationEnd: 0
};
});
For more examples, refer to source code of extension component
Focus on textarea.
import type { FocusOption } from 'md-editor-v3';
const option: FocusOption | undefined = 'start';
// Cursor position when focusing on textarea, default: position when it last lost focus
editorRef.value?.focus(option);
Re render the content.
editorRef.value?.rerender();
Get the currently selected text.
console.log(editorRef.value?.getSelectedText());
Clear current history.
Supports listening to all DOM events.
editorRef.value?.domEventHandlers({
compositionstart: () => {
console.log('compositionstart');
}
});
Insert content into the editor via trigger.
editorRef.value?.execCommand('bold');
Use config(option: ConfigOption)
to reconfigure markdown-it
and so on.
Customize new extensions based on theme and default extensions f codeMirror.
Example: Editor does not render the line number of textarea by default, this extension needs to be manually added
import { config } from 'md-editor-v3';
import { lineNumbers } from '@codemirror/view';
config({
codeMirrorExtensions(_theme, extensions) {
return [...extensions, lineNumbers()];
}
});
Customize extensions, attributes of markdown-it
, etc.
type MarkdownItConfig = (
md: markdownit,
options: {
editorId: string;
}
) => void;
Example: Use markdown-it-anchor
to render a hyperlink symbol to the right of the title
import { config } from 'md-editor-v3';
import ancher from 'markdown-it-anchor';
config({
markdownItConfig(mdit) {
mdit.use(ancher, {
permalink: true
});
}
});
Select and add built-in plugins to markdown-it
.
type MarkdownItPlugins = (
plugins: Array<MarkdownItConfigPlugin>,
options: {
editorId: string;
}
) => Array<MarkdownItConfigPlugin>;
Example: Modify the class name of the image.
import { config } from 'md-editor-v3';
config({
markdownItPlugins(plugins) {
return plugins.map((p) => {
if (p.type === 'image') {
return {
...p,
options: {
...p.options,
classes: 'my-class'
}
};
}
return p;
});
}
});
Add more languages, reset mermaid
template or delay rendering time:
import { config } from 'md-editor-v3';
config({
editorConfig: {
languageUserDefined: { lang: StaticTextDefaultValue },
mermaidTemplate: {
flow: `flow tempalte`,
...more
},
// Default 500ms. It is set to 0ms when preview only and not set.
renderDelay: 500,
// for modal component
zIndex: 2000
}
});
Config some dependency libraries, like highlight..
import { config } from 'md-editor-v3';
config({
editorExtensions: { iconfont: 'https://xxx.cc' }
});
export interface EditorExtensions {
highlight?: {
instance?: any;
js?: string;
css?: {
[key: string]: {
light: string;
dark: string;
};
};
};
prettier?: {
standaloneJs?: string;
parserMarkdownJs?: string;
};
cropper?: {
instance?: any;
js?: string;
css?: string;
};
iconfont?: string;
screenfull?: {
instance?: any;
js?: string;
};
mermaid?: {
instance?: any;
js?: string;
};
katex?: {
instance?: any;
js?: string;
css?: string;
};
}
Synchronously add attributes to the CDN link tags, consistent with the type of editorExtensions
, with a value type of HTMLElementTagNameMap['tagName']
.
import { config, editorExtensionsAttrs } from 'md-editor-rt';
config({
editorExtensionsAttrs
});
Do not attempt to define the src \ onload \ id of the script and rel \ href \ id of the link in editorExtensionsAttrs, as they will be overwritten by default values
Set the way to display icons:
svg
: with symbolclass
: with font-classIf the icon is customized through the attribute customIcon
, the customized icon will be used first.
Configure mermaid
, Details
import { config } from 'md-editor-v3';
config({
mermaidConfig(base: any) {
return {
...base,
logLevel: 'error'
};
}
});
Pay attention: shortcut keys are only available when the textarea has received focus!
key | function | description |
---|---|---|
TAB | insert space | Insert space, the length eq tabWidth , default: 2, support multiline |
SHIFT + TAB | delete space, setting is the same as Tab | |
CTRL + C | copy | When selected, copy the selected content. When not selected, copy the content of the current line |
CTRL + X | shear | When selected, cut the selected content. When not selected, cut the current line |
CTRL + D | delete | When selected, delete the selected content. When not selected, delete the current line |
CTRL + S | save | Trigger onSave event |
CTRL + B | bold text | **bold** |
CTRL + U | underline | <u>underline</u> |
CTRL + I | italic | *italic* |
CTRL + 1-6 | h1-h6 | # title |
CTRL + ↑ | superscript | <sup>superscript</sup> |
CTRL + ↓ | subscript | <sub>subscript</sub> |
CTRL + O | ordered list | 1. ordered list |
CTRL + L | link | [link](https://github.com/imzbf/md-editor-v3) |
CTRL + Z | withdraw | Withdraw history in editor, not the function of system |
CTRL + SHIFT + S | line-through | ~line-through~ |
CTRL + SHIFT + U | unordered list | - unordered list |
CTRL + SHIFT + C | code block | |
CTRL + SHIFT + I | picture | ![picture](https://imzbf.github.io/md-editor-v3/imgs/preview-light.png) |
CTRL + SHIFT + Z | forward | Forward history in editor, not the function of system |
CTRL + SHIFT + F | Beautify | |
CTRL + ALT + C | code row | |
CTRL + SHIFT + ALT + T | table | |table| |
<script lang="ts" setup>
import { NormalToolbar } from 'md-editor-v3';
</script>
On-demand import. For more examples, refer to document.
NormalToolbar
props
title
: string
, not necessary, title of toolbar.events
onClick
: (e: MouseEvent) => void
, necessary.slots
trigger
: VNode | JSX.Element
, necessary, it is usually an icon, which is displayed on the toolbar.DropdownToolbar
props
title
: string
, not necessary, title of toolbar.visible
: boolean
, necessary.events
onChange
: (visible: boolean) => void
, necessary.slots
trigger
: VNode | JSX.Element
, necessary, it is usually an icon, which is displayed on the toolbar.overlay
: VNode | JSX.Element
, necessary, content of dropdown box.ModalToolbar
props
title
: string
, not necessary, title of toolbar.modalTitle
: string
, not necessary, title of the Modal.visible
: boolean
, necessary, visibility of Modal.width
: string
, not necessary, width of Modal, default auto
.height
: string
, same as width
.showAdjust
: boolean
, not necessary, visibility of fullscreen button.isFullscreen
: boolean
, necessary when showAdjust = true
, status of fullscreen.events
onClick
: () => void
, necessary.onClose
: () => void
, necessary, closed event.onAdjust
: (val: boolean) => void
, fullscreen button was clicked.slots
trigger
: VNode | JSX.Element
, necessary, it is usually an icon, which is displayed on the toolbar.default
: VNode | JSX.Element
, necessary, content of Modal.MdCatalog
props
editorId
: string
, necessary, same as editor's editorId
, used to register listening events.class
: string
, not necessary.mdHeadingId
: MdHeadingId
, not necessary, same as editor.scrollElement
: string | HTMLElement
, not necessary, it is an element selector when its type is string. When previewOnly
eq true
, it is usually set to document.documentElement
.theme
: 'light' | 'dark'
, not necessary, provide it when you want to change theme online, it is the same as Editor theme
.offsetTop
: number
, not necessary, highlight current item of catalogs when title is offsetTop
pixels from the top, default 20.scrollElementOffsetTop
: number
, not necessary, offsetTop of the scroll container,default 0.events
onClick
: (e: MouseEvent, t: TocItem) => void
, not necessary, heading was clicked.onActive
: (heading: HeadList | undefined) => void
, not necessary, heading was highlighted.MdModal
props
title
: string
, not necessary, title of Modal.visible
: boolean
, necessary, visibility of Modal.width
: string
, not necessary, width of Modal, default auto
.height
: string
, same as width
.showAdjust
: boolean
, not necessary, visibility of fullscreen button.isFullscreen
: boolean
, necessary when showAdjust = true
, status of fullscreen.className
: string
, not necessary.style
: CSSProperties
, not necessary.events
onClose
: () => void
, necessary, closed event.onAdjust
: (val: boolean) => void
, fullscreen button was clicked.slots
default
: VNode | JSX.Element
, necessary, content of Modal.import {
iconfontClassUrl,
iconfontSvgUrl,
allToolbar,
allFooter,
zh_CN,
en_US
} from 'md-editor-v3';
console.log(iconfontClassUrl, iconfontSvgUrl, allToolbar, allFooter, zh_CN, en_US);
import { defineComponent, reactive } from 'vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
export default defineComponent({
setup() {
const md = reactive({
text: '# Hello Editor'
});
return () => (
<MdEditor modelValue={md.text} onChange={(value) => (md.text = value)} />
);
}
});
Tips: When you paste and upload GIF, it will upload a static picture. So you should upload it by file system!
<template>
<MdEditor v-model="text" @onUploadImg="onUploadImg" />
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const text = ref('# Hello Editor');
const onUploadImg = async (files, callback) => {
const res = await Promise.all(
files.map((file) => {
return new Promise((rev, rej) => {
const form = new FormData();
form.append('file', file);
axios
.post('/api/img/upload', form, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then((res) => rev(res))
.catch((error) => rej(error));
});
})
);
// Approach 1
callback(res.map((item) => item.data.url));
// Approach 2
// callback(
// res.map((item: any) => ({
// url: item.data.url,
// alt: 'alt',
// title: 'title'
// }))
// );
};
</script>
.css-vars(@isDark) {
--md-color: if(@isDark, #999, #222);
--md-hover-color: if(@isDark, #bbb, #000);
--md-bk-color: if(@isDark, #000, #fff);
--md-bk-color-outstand: if(@isDark, #111, #f6f6f6);
--md-bk-hover-color: if(@isDark, #1b1a1a, #f5f7fa);
--md-border-color: if(@isDark, #2d2d2d, #e6e6e6);
--md-border-hover-color: if(@isDark, #636262, #b9b9b9);
--md-border-active-color: if(@isDark, #777, #999);
--md-modal-mask: #00000073;
--md-scrollbar-bg-color: if(@isDark, #0f0f0f, #e2e2e2);
--md-scrollbar-thumb-color: if(@isDark, #2d2d2d, #0000004d);
--md-scrollbar-thumb-hover-color: if(@isDark, #3a3a3a, #00000059);
--md-scrollbar-thumb-active-color: if(@isDark, #3a3a3a, #00000061);
}
.md-editor {
.css-vars(false);
}
.md-editor-dark {
.css-vars(true);
}
Change background color in dark mode:
.md-editor-dark {
--md-bk-color: #333 !important;
}
FAQs
Markdown editor for vue3, developed in jsx and typescript, dark theme、beautify content by prettier、render articles directly、paste or clip the picture and upload it...
We found that md-editor-v3 demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.