While KunEditor is at P0 scaffold,
<KunEditor>renders a textarea fallback that keepsv-modellive. Everything below is the stable contract β code against it now; behavior fills in as the Milkdown ports land (seearchitecture.md).
Install KunEditor plus the Milkdown stack in the host (single ProseMirror
instance β see architecture.md Β§ singleton) and @kungal/ui-vue for the chrome:
# Plain Vue
pnpm add @kungal/editor-vue @kungal/editor-core \
@kungal/ui-vue vue \
@milkdown/kit @milkdown/prose @milkdown/vue
# Optional plugin peers
pnpm add katex # LaTeX
pnpm add codemirror @codemirror/view @codemirror/language-data # code blocksNuxt: also add @kungal/editor-nuxt, @kungal/ui-nuxt, nuxt.
KunEditor is headless β it ships zero CSS, only stable class hooks
(.kun-editor__*, .kun-mention-dropdown*) and data-* state. Give it a look
with your own stylesheet. The canonical reference is
packages/editor-nuxt/editor.css, which
themes the hooks with KunUI tokens and SHIPS with the layer β import it next to
your KunUI entry (or copy it and edit):
/* your app's main.css β after @kungal/ui-tokens + @kungal/ui-vue */
@import '@kungal/editor-nuxt/editor.css'; /* the reference stylesheet */Also import the peers' own CSS for the features you enable:
import 'katex/dist/katex.min.css' // when the katex feature is onThe reference stylesheet carries the structural rules the editor genuinely
needs β ProseMirror white-space, the img.ProseMirror-separator guard (the
caret anchor ProseMirror appends after a trailing @mention / #floor chip; a
generic img { display: block } such as Tailwind's preflight would push the
caret onto a new line), popover positioning / show-hide β so don't drop those
if you write your own.
Adapters are how the editor talks to your backend. All optional β omit one and its plugin disappears.
import type { KunEditorAdapters } from '@kungal/editor-core'
const adapters: KunEditorAdapters = {
// Upload one image, return the URL to embed. Called per image for toolbar
// pick / paste / drop. Reject to abort that image.
uploadImage: async (file) => {
const form = new FormData()
form.append('image', file)
return await api.post<string>('/image/topic', form)
},
// Resolve @mention queries. Debouncing is handled inside the editor.
searchMentionUsers: (q) => oauth.searchUsers(q), // β { id, name, avatar? }[]
// Optional: sticker packs for the picker. Omit to hide the sticker UI.
stickerSource: () => stickerStore.packs,
// Optional: route editor notices to your toast system.
notify: (message, level) => toast[level](message)
}<script setup lang="ts">
import { KunEditor } from '@kungal/editor-vue'
import '@kungal/editor-nuxt/editor.css' // the reference stylesheet
const markdown = ref('')
</script>
<template>
<KunEditor v-model="markdown" :adapters="adapters" locale="zh-cn" />
</template>// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@kungal/ui-nuxt', '@kungal/editor-nuxt']
})<!-- no import needed -->
<KunEditor v-model="markdown" :adapters="adapters" />features turns optional plugins off even when their adapter is present β but an
absent adapter always wins (you can't enable mention without
searchMentionUsers).
<KunEditor
v-model="markdown"
:adapters="adapters"
:features="{ katex: false, sticker: false }"
/>Entity cards / chips need entityFromUrl (absent β links stay links); the /
menu and the toolbar picker additionally need searchEntities + entityTypes.
The host renders them: <KunEditor :entity-view="{ card: TopicCard, chip: TopicChip }">.
The image-free editor (e.g. a short intro field) is just an omitted
uploadImage β no flag needed; upload, paste/drop and sticker paths all drop
out together.
| Prop | Type | Default | Notes |
|---|---|---|---|
v-model |
string |
β | bound markdown |
adapters |
KunEditorAdapters |
{} |
policy: upload / mention / sticker / notify |
features |
KunEditorFeatures |
{} |
per-plugin on/off (absent adapter wins) |
locale |
'zh-cn' | 'en-us' | string |
'zh-cn' |
toolbar labels / placeholders |
readonly |
boolean |
false |
render without the toolbar / editing |
Like KunUI, KunEditor does not sanitize HTML. It produces and consumes
markdown; rendering that markdown to HTML (and sanitizing it) is the host's job,
server-side, exactly as the forum does today. The kungal-user: mention scheme
is intentionally a non-navigating link the server resolves.