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
import { createSignal } from "solid-js";
import { MdEditor } from "@hiveio/honeycomb-solid";
function PostEditor() {
const [content, set_content] = createSignal("");
return <MdEditor value={content()} onChange={set_content} />;
}Preview
Examples
Hive Image Upload
Use create_hive_upload_handler to upload images to the Hive imagehoster with a signing challenge.
import { createSignal, createMemo } from "solid-js";
import { MdEditor, create_hive_upload_handler } from "@hiveio/honeycomb-solid";
function PostEditor(props: { username: string }) {
const [content, set_content] = createSignal("");
const upload_handler = createMemo(() =>
create_hive_upload_handler({
imageEndpoint: "https://images.hive.blog/",
username: props.username,
signChallenge: async (challenge) => {
return await keychain.requestSignBuffer(props.username, challenge);
},
}),
);
return (
<MdEditor
value={content()}
onChange={set_content}
config={{ uploadHandler: upload_handler() }}
/>
);
}Preview Modes
"split"Editor and preview side by side
"tab"Switchable Write/Preview tabs
"off"Editor only (default)
<MdEditor
value={content()}
onChange={set_content}
config={{ previewMode: "split" }}
/>
{/* "tab" - switchable Write/Preview tabs */}
<MdEditor
value={content()}
onChange={set_content}
config={{ previewMode: "tab" }}
/>Custom Toolbar
Filter or reorder DEFAULT_TOOLBAR to create a minimal editor.
import { MdEditor, DEFAULT_TOOLBAR } from "@hiveio/honeycomb-solid";
import type { ToolbarItem } from "@hiveio/honeycomb-solid";
const minimal_toolbar: ToolbarItem[] = DEFAULT_TOOLBAR.filter(
(item) =>
item.type === "bold" ||
item.type === "italic" ||
item.type === "link" ||
item.type === "image" ||
item.type === "separator",
);
function MinimalEditor() {
const [content, set_content] = createSignal("");
return (
<MdEditor
value={content()}
onChange={set_content}
config={{ toolbar: minimal_toolbar }}
/>
);
}Draft Auto-save
Enable draft auto-save to localStorage with configurable debounce and TTL.
<MdEditor
value={content()}
onChange={set_content}
config={{
draftConfig: {
enabled: true,
key: "post-draft-my-post",
debounceMs: 1000,
ttlMs: 7 * 24 * 60 * 60 * 1000,
},
}}
/>Dark Mode
The "auto" value follows the system preference via prefers-color-scheme.
// "auto" follows system preference (default)
<MdEditor value={content()} onChange={set_content} config={{ theme: "auto" }} />
// Force light or dark
<MdEditor value={content()} onChange={set_content} config={{ theme: "dark" }} />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 | - |