Markdown Editor
CodeMirror-based Markdown editor with toolbar, live preview, image upload, draft auto-save, and Hive blockchain integration.
Full Toolbar
Bold, italic, headings, lists, tables, code blocks, links, images, spoilers
Live Preview
Split, tab, or off -- three preview modes
Hive Image Upload
Upload to Hive imagehoster with signing challenge
Draft Auto-save
LocalStorage drafts with debounce and TTL
Dark Mode
Auto, light, or dark theme (one-dark-pro)
Plugin System
Custom toolbar items, keyboard shortcuts, paste handlers
CodeMirror peer dependencies
The editor uses CodeMirror 6 internally. You need to install the CodeMirror packages as peer dependencies alongside the honeycomb package.
Installation
pnpm add @codemirror/state @codemirror/view @codemirror/language @codemirror/lang-markdown @codemirror/commands @codemirror/theme-one-darkBasic Usage
<template>
<MdEditor :value="content" @change="(val) => (content = val)" />
</template>
<script setup lang="ts">
import { ref } from "vue";
import { MdEditor } from "@hiveio/honeycomb-vue";
const content = ref("");
</script>Preview
Examples
Hive Image Upload
Use create_hive_upload_handler to upload images to the Hive imagehoster with a signing challenge.
<template>
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{ uploadHandler: upload_handler }"
/>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import { MdEditor, create_hive_upload_handler } from "@hiveio/honeycomb-vue";
const props = defineProps<{ username: string }>();
const content = ref("");
const upload_handler = computed(() =>
create_hive_upload_handler({
imageEndpoint: "https://images.hive.blog/",
username: props.username,
signChallenge: async (challenge) => {
return await keychain.requestSignBuffer(props.username, challenge);
},
}),
);
</script>Preview Modes
"split"Editor and preview side by side
"tab"Switchable Write/Preview tabs
"off"Editor only (default)
<template>
<!-- Side-by-side editor and preview -->
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{ previewMode: 'split' }"
/>
<!-- Switchable Write/Preview tabs -->
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{ previewMode: 'tab' }"
/>
</template>Custom Toolbar
Filter or reorder DEFAULT_TOOLBAR to create a minimal editor.
<template>
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{ toolbar: minimal_toolbar }"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { MdEditor, DEFAULT_TOOLBAR } from "@hiveio/honeycomb-vue";
const content = ref("");
const minimal_toolbar = DEFAULT_TOOLBAR.filter(
(item) =>
item.type === "bold" ||
item.type === "italic" ||
item.type === "link" ||
item.type === "image" ||
item.type === "separator",
);
</script>Draft Auto-save
Enable draft auto-save to localStorage with configurable debounce and TTL.
<template>
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{
draftConfig: {
enabled: true,
key: 'post-draft-my-post',
debounceMs: 1000,
ttlMs: 7 * 24 * 60 * 60 * 1000,
},
}"
/>
</template>Dark Mode
The "auto" value follows the system preference via prefers-color-scheme.
<template>
<!-- "auto" follows system preference (default) -->
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{ theme: 'auto' }"
/>
<!-- Force dark -->
<MdEditor
:value="content"
@change="(val) => (content = val)"
:config="{ theme: 'dark' }"
/>
</template>Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Markdown content (controlled) |
onChange | (value: string) => void | - | Called on content change |
config | Partial<MdEditorConfig> | See config table | Editor configuration |
class | string | - | CSS class on wrapper element |
rendererOptions | Partial<RendererOptions> | - | Options passed to the preview renderer |
onFocus | () => void | - | Called when editor gains focus |
onBlur | () => void | - | Called when editor loses focus |
onUploadStart | (file: File) => void | - | Called when image upload starts |
onUploadComplete | (result: UploadResult) => void | - | Called when upload succeeds |
onUploadError | (error: Error) => void | - | Called when upload fails |
onDraftSave | (draft: DraftData) => void | - | Called when draft is saved |
onDraftRestore | (draft: DraftData) => void | - | Called when draft is restored on mount |
MdEditorConfig
Pass via the config prop. All fields are optional.
| Field | Type | Default | Description |
|---|---|---|---|
placeholder | string | - | Placeholder text |
minHeight | number | 300 | Minimum height in pixels |
maxHeight | number | - | Maximum height in pixels |
autoFocus | boolean | false | Focus editor on mount |
toolbar | ToolbarItem[] | DEFAULT_TOOLBAR | Custom toolbar actions |
plugins | EditorPlugin[] | [] | Editor plugins (toolbar items, shortcuts, paste handlers) |
uploadHandler | UploadHandler | - | Image upload handler (enables upload button) |
draftConfig | DraftConfig | - | Auto-save draft configuration |
previewMode | "split" | "tab" | "off" | "off" | Initial preview mode |
theme | "light" | "dark" | "auto" | "auto" | Editor color theme |
convertHiveUrls | boolean | false | Auto-convert Hive blog URLs to @mention/permlink format |
Default Toolbar Actions
Keyboard shortcuts use Mod (Cmd on macOS, Ctrl on Windows/Linux).
| Action | Type | Shortcut |
|---|---|---|
| Bold | bold | Mod-B |
| Italic | italic | Mod-I |
| Strikethrough | strikethrough | Mod-Shift-S |
| Inline Code | code | Mod-E |
| Heading | heading | - |
| Quote | quote | - |
| Horizontal Rule | horizontal_rule | - |
| Code Block | code_block | - |
| Table | table | - |
| Link | link | Mod-K |
| Image | image | - |
| Spoiler | spoiler | - |
| Unordered List | unordered_list | - |
| Ordered List | ordered_list | - |
| Task List | task_list | - |