Skip to content

Markdown Decoder Plugins ​

PowerEditor includes built-in Markdown import and export support:

  • Use editor.insertMarkdown(markdown) to import Markdown into the editor
  • Use editor.saveMarkdown() to export the current editor content as Markdown
  • Use mdDecNodeFuncsPlugins and mdFlags to customize how specific nodes and marks are rendered during export

Try It Online ​

Markdown Import And Export ​

Import Markdown ​

Use the exposed insertMarkdown(markdown) method to parse a Markdown string and write it into the editor.

vue
<script setup>
import { ref } from "vue";

const editor = ref(null);

const importMarkdown = () => {
    const markdown = `# Hello PowerEditor

- item 1
- item 2
`;

    editor.value?.insertMarkdown(markdown);
};
</script>

<template>
    <fv-button @click="importMarkdown">Import Markdown</fv-button>
    <power-editor ref="editor" />
</template>

When importing from a file, the usual flow is to read the file text first and then call:

js
const markdown = await file.text();
editor.value?.insertMarkdown(markdown);

Export Markdown ​

Use saveMarkdown() to convert the current editor content into a Markdown string.

vue
<script setup>
import { ref } from "vue";

const editor = ref(null);

const exportMarkdown = () => {
    const markdown = editor.value?.saveMarkdown?.() ?? "";
    console.log(markdown);
};
</script>

<template>
    <fv-button @click="exportMarkdown">Export Markdown</fv-button>
    <power-editor ref="editor" />
</template>

If you want to download the result directly as a file:

js
const markdown = editor.value?.saveMarkdown?.() ?? "";
const blob = new Blob([markdown], { type: "text/markdown;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");

link.href = url;
link.download = "power-editor.md";
link.click();

URL.revokeObjectURL(url);
MethodDescription
editor.insertMarkdown(markdown)Parses Markdown and writes it into the editor.
editor.saveMarkdown()Exports the current editor content as a Markdown string.
editor.computeMarkdown(markdown)Only parses Markdown and returns editor-friendly content data without writing it into the editor.

How To Customize Export Output ​

During Markdown export, the decoder traverses the ProseMirror document recursively in depth-first order. Built-in nodes use default decoder functions. You only need custom plugin functions for custom nodes or output formats that need special handling.

Plugin function names must match node names or mark names. A function can return a string, or an object with prefix and suffix.

ts
type DecoderResult = string | {
    prefix: string;
    suffix: string;
};

Custom Nodes ​

The example below renders blockquote with the correct number of > prefixes based on nesting depth.

markdown
> Level one
>> Level two
>>> Level three
js
blockquote(node, flags) {
    const { blockquote: level } = flags;
    let prefix = "";

    for (let i = 0; i < level; i++) {
        prefix += ">";
    }

    return `\n${prefix} `;
}

A blockquote in PowerEditor may contain child nodes such as paragraph and text. In most cases, those children do not need to be handled manually, because the decoder continues with its default recursive rules.

Use Flags For Nesting ​

flags records which parent nodes the current node is inside and how deeply it is nested. Default flags usually look like this:

js
const flags = {
    inline: false,
    inlineWrapper: false,
    heading: false,
    bulletList: false,
    orderedList: false,
    blockquote: false,
    powerTaskItem: false,
    powerTaskList: false,
    tableHeader: false,
    tableCell: false,
    tableRow: false,
    table: false
};

When traversal enters a tracked node, its flag changes from false to 1. If traversal enters a nested node of the same type, the value increments to 2, 3, and so on.

Pass Plugins To PowerEditor ​

vue
<power-editor
    ref="editor"
    :md-dec-node-funcs-plugins="mdDecNodeFuncsPlugins"
    :md-flags="mdFlags"
/>
js
export default {
    data() {
        return {
            mdDecNodeFuncsPlugins: {
                blockquote: (node, flags) => {
                    const { blockquote: level } = flags;
                    let prefix = "";

                    for (let i = 0; i < level; i++) {
                        prefix += ">";
                    }

                    return `\n${prefix} `;
                }
            },
            mdFlags: {
                blockquote: false
            }
        };
    }
};

Then export with:

js
const markdown = editor.value?.saveMarkdown?.() ?? "";

The resulting Markdown will use your custom rules.

Custom Marks ​

Mark plugins follow the same naming rule: the function name must match the mark name. The example below outputs the color value from textStyle as an HTML font tag:

markdown
<font color="red">Red text</font>
js
textStyle(text, mark) {
    const { color } = mark.attrs;

    return {
        prefix: `<font color="${color}">`,
        suffix: "</font>"
    };
}

When an object is returned, prefix is written before the text and suffix after it. This is useful for paired Markdown or HTML syntax.

MIT Licensed