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