@mdefy/markdown-editor-core
v1.0.1
Published
A JS Markdown editor with no fancy UI but an awesome API
Downloads
40
Maintainers
Readme
Markdown Editor Core
Markdown Editor Core is a WYSIWYG markdown editor based on the JavaScript text editor CodeMirror.
The editor shipped with this library can be used standalone to write Markdown in an efficient way with nice highlighting. However, this library is mainly intended to work as a universal core component for other Markdown Editors, that are e.g. implemented for big JS-based frameworks or other JS systems. Therefore, Markdown Editor Core provides a simple and clear API for all common actions necessary when writing markdown and the editor is highly configurable.
The goal of this library is to provide a fully working text editor which can be controlled by its API, without establishing a specific view or adding further visual components like toolbar buttons as known from other fully-WYSIWYG editors. This makes it easily extensible and customizable for your needs, while setting you free from thinking about text manipulation.
Markdown Editor Core was developed for and in parallel with Ngx Markdown Editor. Thus, the latter is one example of how this library can be used. In the same way, components could be implemented for React or Vue or your custom JS app.
Table of contents
- How to install
- How to use
- Configuration options
- Shortcuts
- Theming
- How to contribute
- Project setup
- A word on tests
How to install
Run
npm i markdown-editor-core
or
yarn add markdown-editor-core
Load CodeMirror's stylesheet for its default theme and other required stylings; e.g. by including it into your index.html
:
<link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css" />
How to use
To instantiate MarkdownEditor
, you must specify a wrapper element and you can pass an optional configuration object.
const wrapper = document.getElementById('my-wrapper-element') as HTMLElement; // required
const options: MarkdownEditorOptions = { ... }; // optional
const mde = new MarkdownEditor(wrapper, options);
You can also replace an existing textarea with the Markdown Editor.
const textarea = document.getElementById('my-textarea') as HTMLTextareaElement; // required
const options: MdeFromTextareaOptions = { ... }; // optional
const mde = new MarkdownEditorFromTextarea(textarea, options);
It is possible to synchronize the editor's content with the content of the textarea in two ways:
- either manually via
mde.syncTextarea()
- or automatically by setting the option
autoSync
totrue
.
You can also switch back to the textarea via mde.toTextarea()
(this destroys the Markdown Editor instance).
Configuration options
Shortcuts
The default keymap is as follows (on Mac "Ctrl" is replaced with "Cmd"):
| Action | Shortcut |
| ---------------------- | ---------------- |
| increaseHeadingLevel
| Alt-H |
| decreaseHeadingLevel
| Shift-Alt-H |
| toggleBold
| Ctrl-B |
| toggleItalic
| Ctrl-I |
| toggleStrikethrough
| Ctrl-K |
| toggleUnorderedList
| Ctrl-L |
| toggleOrderedList
| Shift-Ctrl-L |
| toggleCheckList
| Shift-Ctrl-Alt-L |
| toggleQuote
| Ctrl-Q |
| insertLink
| Ctrl-M |
| insertImageLink
| Shift-Ctrl-M |
| insertTable
| Ctrl-Alt-T |
| insertHorizontalRule
| Shift-Ctrl-- |
| toggleInlineCode
| Ctrl-7 |
| insertCodeBlock
| Shift-Ctrl-7 |
| openMarkdownGuide
| F1 |
| toggleRichTextMode
| Alt-R |
| downloadAsFile
| Shift-Ctrl-S |
| importFromFile
| Ctrl-Alt-I |
| formatContent
| Alt-F |
You can customize the individual shortcuts inside MarkdownEditorOptions
via options.shortcuts
.
For shortcuts that come built-in with CodeMirror, see CodeMirror documentation.
If you want to specify your own shortcuts via CodeMirror, mind the correct order of special keys: Shift-Cmd-Ctrl-Alt (see here).
You can add new shortcuts using mde.addShortcut(hotkeys, void)
or remove existing ones using mde.removeShortcut(hotkeys)
.
Theming
How to change the editor's styling
The editor's view can be customized using CodeMirror themes.
The default theme of CodeMirror is "default" (results in the class .cm-s-default
), which basically presents a blank editor and defines the default styles for the markup highlighting.
To apply a customized theme with the name "example"
- specify
{ theme: 'example' }
in theMarkdownEditorOptions
, - define the CSS class
.cm-s-example
in a CSS file, and - make sure to load the CSS file with your app.
With such a theme you can customize CodeMirror's visual appearance and behavior. For further details visit the dedicated section on CodeMirror.
If you only want to extend the default theme, you can either define new stylings for the class .cm-s-default
and make sure that the "default" theme is applied or you can create your own additional theme and specify two themes in the MarkdownEditorOptions
: { theme: 'default additional-theme' }
.
How to change the markup styling (e.g. heading, bold, ...)
The markup stylings work with CodeMirror classes as well and can (and should!) therefore be part of a CodeMirror theme.
If you want to change the styling of "bold" markup for example, then define a new style for .cm-bold
. Again, this should
preferably be done within a theme (also see "How to use your own theme").
The classes for markup styling are:
| Markup type | Class |
| ------------------------------- | --------------------------------- |
| Heading | .cm-header
|
| Bold | .cm-bold
|
| Italic | .cm-italic
|
| Strikethrough | .cm-strikethrough
|
| List level 1 | .cm-list-level-1
, .cm-list
|
| List level 2 | .cm-list-level-2
, .cm-list
|
| List level > 2 | .cm-list-level-gt-2
, .cm-list
|
| Quote | .cm-quote
|
| Link (hyperlink in general) | .cm-link
|
| Link text (part inside "[...]") | .cm-link-text
|
| Link href (part inside "(...)") | .cm-link-href
, .cm-link
|
| Email link | .cm-link-email
, .cm-link
|
| Inline link ("<http://...>") | .cm-link-inline
, .cm-link
|
| Image | .cm-image
|
| Image alt text | .cm-image-alt-text
|
| Image marker ("!") | .cm-image-marker
|
| Horizontal rule | .cm-hr
|
| Code | .cm-code
|
| Emoji | .cm-emoji
|
| Tokens | .cm-token
|
The last table row entry "tokens" refers to all markup tokens like **, _, [], (), etc. and only
applies if highlightTokens
is enabled in the MarkdownEditorOptions
. If this is true, then
all tokens have the class .cm-token
. Additionally every token is given an individual class
corresponding to the markup type to which it belongs to and eventually a "token level class". This means, you can easily style all tokens in the same way or each token type individually.
Examples:
- The ** tokens for bold text have the classes
cm-strong cm-token cm-token-strong
- A > token for quotation in the second level (second token of >>) has the classes
cm-quote cm-quote-2 cm-token cm-token-quote cm-token-quote-2
.
Here is a list of all CodeMirror token classes:
| Token type | Class |
| ------------------------------ | --------------------------------------------------------------------------------- |
| Heading #
| .cm-token-header
, .cm-token-header-[n]
|
| Bold **
| .cm-token-strong
|
| Italic _
| .cm-token-em
|
| Strikethrough ~~
| .cm-token-strikethrough
|
| Unordered list -
| .cm-token-list
, .cm-token-list-ul
|
| Ordered list 1.
| .cm-token-list
, .cm-token-list-ol
|
| Checklist - [x]
| .cm-token-list
, .cm-token-list-ul
("-" token); .cm-token-task
("[x]" token) |
| Quote >
| .cm-token-quote
, .cm-token-quote-[n]
|
| Link []()
| .cm-token-link
("[]" token); .cm-token-link-string
("()" token) |
| Image ![]()
| .cm-token-image
("![]" token); .cm-token-link-string
("()" token) |
| Inline Code ` | .cm-token-code
|
| Code block ``` | .cm-token-code-block
|
How to contribute
First of all, contributions in any way are very welcome! And a big thank you to all who decide to so!! :)
The code is neither perfect nor complete. If you have any suggestions, requirements or even just comments, please let me know and I will do my best do incorporate them! The even better (and probably faster) way for requesting code modifications, however, are pull requests. I am very happy about all code contributions as time is often rare around here... :)
Writing issues
When writing issues, please give a clear description of the current state and what you are unhappy about. Then, if possible, propose your solution or at least leave a short statement of your thoughts about it.
Making pull requests
Recipe for making a pull request:
- Fork and checkout repo locally.
- Install Yarn, if you do not have it yet. For example via
npm i yarn -g
. - Open a command line, move to the project directory and run
yarn
to install all dependencies. - Make your code changes. (Please mind the style guidelines.)
- Use
yarn run start
to test your changes in the demo app. - Check the docs whether they need to be changed.
- Push the changes to your fork.
- Make a pull request to the master branch of this repo. Please provide a meaningful title for the PR and give a concise description.
Project setup
Package manager
This project uses Yarn as package manager. So you must use this one to install dependencies when contributing code. The scripts in package.json still work with npm
, although it is recommended to always use yarn
throughout the project.
FYI: The main reason to
move from npm to Yarn was, that Yarn is able to execute shell scripts platform-independent in the native console.
I.e. it also understands paths with forward slashes like ./path/to/script.sh
on windows and can execute it inside CMD.
My claim is to provide a platform-independent project setup and the described issue comes into effect, for example, when running the build
script in package.json.
Commit rules
We use Commitlint to guarantee structured commit messages.
This means you must write commit messages that meet the rules of Commitlint.
If you are not familiar with Commitlint, you can use the CLI tool Commitizen by running yarn run commit
, which assists you to
write conventional messages.
You can also install Commitizen globally on your system, if you want to use the shorter cli commands cz
or git cz
.
Coding style guidelines
There are not many strict guidelines to keep in mind, but please adapt to the project's code style when contributing. Only two more things shall be mentioned here:
We use Prettier to ensure consistent formatting! Therefore, you should install a Prettier plugin for your IDE. Further it is highly recommended to enable "Format on save", which is also set as the project's default for VSCode.
There is a pre-commit git hook for Prettier, which checks the formatting of all files. Occasionally it might happen that this hook fails although you have "Format on save" enabled. This is usually due to wrong line endings, e.g. caused by
yarn add ...
or some other file-writing script or tool. In this case, runyarn run format:write
to let Prettier correct the wrong formatting and then try to commit again. Unfortunately, theformat:write
command cannot be set as a pre-commit hook as it is not known in general, which files need to be staged afterwards.The methods in
markdown-editor.ts
are grouped into 5 sections as you can see when inspecting the file. Please put new methods at the end of the corresponding section:- Basic Editor API: basic actions like
toggleBold
- Extended Editor API: extended actions like
downloadAsFile
, usually do not change the content - Developer API: methods useful for developers using this library
- Markdown Editor Options: methods for getting or setting
MarkdownEditorOptions
- Private methods: all private methods (all methods in previous sections should be
public
orprotected
)
- Basic Editor API: basic actions like
A word on tests
As you might have noticed, this project does not contain any tests. Well yes, I have noticed that, too... and I really hope to be able to add tests in the future some time. However, it has not been very easy to decide what to test and what not so far. Because it is a highly interactive application, it contains a lot of edge cases, far more than standard cases (of which most are directly visible to human's eye anyway). Especially the multiple-cursor mode of CodeMirror increases the number of test branches tremendously. In addition, it is quite hard to draw a line between testing the Markdown Editor (which is the goal) and testing CodeMirror, which is already tested quite well.
Those issues are clearly not an excuse to omit tests totally, but they drove me to the decision to postpone writing tests a bit as I wanted to finally come to the point, where this project is ready for release. However, this is why it was even more important for me to provide a detailed documentation both in code files and in this Readme.
Finally, due to the high number of edge cases, I would like to encourage you again to contribute - either by writing issues or by explicitly fixing things in code - whenever you discover bugs or odd behavior! I believe, helping each other out by quickly pointing those problems is a very good and also effective way, too, in order to improve an applications quality.