### Install Highlight Plugin
Source: https://milkdown.dev/docs/guide/code-highlighting
Install the highlight plugin using npm.
```bash
npm install @milkdown/plugin-highlight
```
--------------------------------
### Plugin Example
Source: https://milkdown.dev/docs/api/ctx
Demonstrates the structure of a Milkdown plugin, including setup, run, and optional cleanup phases. Plugins can be synchronous or asynchronous.
```typescript
// A full plugin example
const plugin1 = (ctx: Ctx) => {
// setup
return async () => {
// run
return async () => {
// cleanup
}
}
}
// A plugin doesn't need to return a cleanup function
const plugin2 = (ctx: Ctx) => {
// setup
return async () => {
// run
}
}
// A plugin doesn't need to be async
const plugin3 = (ctx: Ctx) => {
// setup
return () => {
// run
}
}
```
--------------------------------
### Example Plugin Lifecycle
Source: https://milkdown.dev/docs/guide/architecture-overview
Shows the typical lifecycle of a Milkdown plugin, including setup, initialization, runtime, and cleanup phases, demonstrating state and dependency management.
```typescript
const examplePlugin: MilkdownPlugin = (ctx) => {
// 1. Setup Phase
ctx.inject(mySlice, defaultValue)
ctx.record(myTimer)
return async () => {
// 2. Initialization Phase
await ctx.wait(RequiredTimer)
// 3. Runtime Phase
const value = ctx.get(mySlice)
ctx.set(mySlice, newValue)
// 4. Cleanup Phase
return () => {
ctx.remove(mySlice)
}
}
}
```
--------------------------------
### Install Crepe and React Packages
Source: https://milkdown.dev/docs/recipes/react
Install the necessary packages for using Crepe, the React integration, and Milkdown kits.
```bash
npm install @milkdown/crepe @milkdown/react @milkdown/kit
```
--------------------------------
### Install Core Milkdown and Theme Packages
Source: https://milkdown.dev/docs/recipes/vue
Install the core Milkdown packages along with a theme for basic editor usage.
```bash
npm install @milkdown/vue @milkdown/kit @milkdown/theme-nord
```
--------------------------------
### Install @milkdown/kit
Source: https://milkdown.dev/docs/guide/getting-started
Install the core Milkdown package for building a custom editor from scratch.
```bash
npm install @milkdown/kit
```
--------------------------------
### Complete Editor Example with Shiki
Source: https://milkdown.dev/docs/guide/code-highlighting
A full example demonstrating the creation of a Milkdown editor with Shiki syntax highlighting configured. This includes setting up the parser and applying the highlight plugin.
```typescript
import { Editor } from '@milkdown/core'
import { commonmark } from '@milkdown/preset-commonmark'
import { highlight, highlightPluginConfig } from '@milkdown/plugin-highlight'
import { createParser } from '@milkdown/plugin-highlight/shiki'
async function createHighlightedEditor() {
const parser = await createParser({
theme: 'github-light',
langs: ['javascript', 'typescript', 'python', 'html', 'css', 'json'],
})
const editor = Editor.make()
.config((ctx) => {
ctx.set(highlightPluginConfig.key, { parser })
})
.use(commonmark)
.use(highlight)
await editor.create()
return editor
}
```
--------------------------------
### Basic Milkdown Editor Implementation in React
Source: https://milkdown.dev/docs/recipes/react
A minimal example demonstrating the core Milkdown editor setup in a React component.
```tsx
import { Editor, rootCtx } from '@milkdown/kit/core'
import { commonmark } from '@milkdown/kit/preset/commonmark'
import { Milkdown, MilkdownProvider, useEditor } from '@milkdown/react'
import { nord } from '@milkdown/theme-nord'
const MilkdownEditor: React.FC = () => {
const { get } = useEditor((root) =>
Editor.make()
.config(nord)
.config((ctx) => {
ctx.set(rootCtx, root)
})
.use(commonmark)
)
return
}
export const MilkdownEditorWrapper: React.FC = () => {
return (
)
}
```
--------------------------------
### Install Crepe and Vue Packages
Source: https://milkdown.dev/docs/recipes/vue
Install the necessary Milkdown packages for Crepe and Vue integration.
```bash
npm install @milkdown/crepe @milkdown/vue @milkdown/kit
```
--------------------------------
### Install Crepe Editor
Source: https://milkdown.dev/docs/guide/using-crepe
Install the Crepe editor package using npm, yarn, or pnpm.
```bash
# Using npm
npm install @milkdown/crepe
# Using yarn
yarn add @milkdown/crepe
# Using pnpm
pnpm add @milkdown/crepe
```
--------------------------------
### Install @milkdown/crepe
Source: https://milkdown.dev/docs/guide/getting-started
Install the ready-to-use Milkdown editor package using npm.
```bash
npm install @milkdown/crepe
```
--------------------------------
### Install Milkdown React and Kit Packages
Source: https://milkdown.dev/docs/recipes/react
Install the core Milkdown React integration and kit packages for basic usage.
```bash
npm install @milkdown/react @milkdown/kit
```
--------------------------------
### Shiki Highlighter Setup
Source: https://milkdown.dev/docs/api/plugin-highlight
Import and create a Shiki highlighter instance. Ensure themes and languages are specified.
```typescript
// For shiki
import { getSingletonHighlighter } from 'shiki'
import { createParser } from '@milkdown/plugin-highlight/shiki'
const highlighter = await getSingletonHighlighter({
themes: ['github-light'],
langs: ['javascript', 'typescript', 'python'],
})
const parser = createParser(highlighter)
```
--------------------------------
### Refractor Highlighter Setup
Source: https://milkdown.dev/docs/api/plugin-highlight
Import and create a Refractor highlighter instance using Prism.js.
```typescript
// For refractor
import { refractor } from 'refractor/all'
import { createParser } from '@milkdown/plugin-highlight/refractor'
const parser = createParser(refractor)
```
--------------------------------
### Lowlight Highlighter Setup
Source: https://milkdown.dev/docs/api/plugin-highlight
Import and create a Lowlight highlighter instance. Requires CSS import for styles.
```typescript
// For lowlight
import 'highlight.js/styles/default.css'
import { common, createLowlight } from 'lowlight'
import { createParser } from '@milkdown/plugin-highlight/lowlight'
const lowlight = createLowlight(common)
const parser = createParser(lowlight)
```
--------------------------------
### Highlight Plugin Setup
Source: https://milkdown.dev/docs/api/plugin-highlight
Demonstrates how to set up the highlight plugin with different parser configurations for Shiki, Lowlight, Refractor, and Sugar High, and then integrate it into a Milkdown editor.
```APIDOC
## Highlight Plugin Setup
### Description
This section shows how to configure and use the Milkdown highlight plugin with various syntax highlighting libraries.
### Methods
- `highlight`: The Milkdown highlight plugin.
- `highlightPluginConfig`: Configuration object for the highlight plugin.
### Usage Examples
#### Using Shiki
```typescript
import { getSingletonHighlighter } from 'shiki'
import { createParser } from '@milkdown/plugin-highlight/shiki'
const highlighter = await getSingletonHighlighter({
themes: ['github-light'],
langs: ['javascript', 'typescript', 'python'],
})
const parser = createParser(highlighter)
```
#### Using Lowlight
```typescript
import 'highlight.js/styles/default.css'
import { common, createLowlight } from 'lowlight'
import { createParser } from '@milkdown/plugin-highlight/lowlight'
const lowlight = createLowlight(common)
const parser = createParser(lowlight)
```
#### Using Refractor
```typescript
import { refractor } from 'refractor/all'
import { createParser } from '@milkdown/plugin-highlight/refractor'
const parser = createParser(refractor)
```
#### Using Sugar High
```typescript
import { createParser } from '@milkdown/plugin-highlight/sugar-high'
const parser = createParser()
```
#### Editor Setup
```typescript
import { Editor } from '@milkdown/core'
import { highlight, highlightPluginConfig } from '@milkdown/plugin-highlight'
// Assuming 'parser' is one of the parser instances created above
Editor.make()
.config((ctx) => {
ctx.set(highlightPluginConfig.key, { parser })
})
.use(highlight)
.create()
```
### Parameters
#### highlightPluginConfig
- **parser** (object) - Required - The parser instance for the chosen highlighting library.
```
--------------------------------
### OpenAI Provider Configuration Example
Source: https://milkdown.dev/docs/api/crepe
Example of configuring the OpenAI provider, including custom body parameters and a custom messages builder. The `buildMessages` function allows for fine-grained control over the message array, respecting the `systemPrompt`'s nullability.
```typescript
// OpenAI: any chat-completions body fields (temperature, top_p, etc.)
// can go in `body`. `buildMessages` lets you fully customize the
// messages array — the defaults are passed in so you can wrap them.
// `defaults.systemPrompt` is `string | null`: `null` means the user
// asked to omit the system message, so don't coerce it to ''.
```
```typescript
createOpenAIProvider({
apiKey,
model: 'gpt-4o-mini',
body: { temperature: 0.2 },
buildMessages: (context, defaults) => [
...(defaults.systemPrompt !== null
? [{ role: 'system' as const, content: defaults.systemPrompt }]
: []),
{ role: 'user', content: defaults.userMessage },
],
})
```
--------------------------------
### Collab Plugin Setup and Usage
Source: https://milkdown.dev/docs/api/plugin-collab
Demonstrates how to set up and use the collab plugin for collaborative editing with Yjs.
```APIDOC
## Collab Plugin Setup and Usage
### Description
This section shows how to integrate the collab plugin into your Milkdown editor and configure it for collaborative editing using Yjs.
### Method
```typescript
import { collab, collabServiceCtx } from '@milkdown/plugin-collab'
import { Editor } from '@milkdown/core'
import { Doc } from 'yjs'
import { WebsocketProvider } from 'y-websocket'
async function setup() {
const editor = await Editor.make().use(collab).create()
const doc = new Doc()
const wsProvider = new WebsocketProvider('', 'milkdown', doc)
editor.action((ctx) => {
const collabService = ctx.get(collabServiceCtx)
collabService
// bind doc and awareness
.bindDoc(doc)
.setAwareness(wsProvider.awareness)
// connect yjs with milkdown
.connect()
})
}
```
### Parameters
N/A (This is a setup example)
### Request Example
N/A
### Response
N/A
```
--------------------------------
### Create Custom Command with Shortcut
Source: https://milkdown.dev/docs/guide/keyboard-shortcuts
Example of creating a custom command and then defining a keymap for it, allowing multiple shortcuts.
```typescript
import { $command, $useKeymap } from '@milkdown/utils'
import { commandsCtx } from '@milkdown/core'
// Create a custom command
const customCommand = $command('CustomCommand', (ctx) => () => {
return (state, dispatch) => {
// Command implementation
return true
}
})
// Create a keymap
const customKeymap = $useKeymap('customKeymap', {
CustomCommand: {
shortcuts: ['F1', 'Mod-F1'], // Multiple shortcuts
command: (ctx) => {
const commands = ctx.get(commandsCtx)
return () => commands.call(customCommand.key)
},
},
})
// Usage
Editor.make().use(customCommand).use(customKeymap)
```
--------------------------------
### Create Tooltip View
Source: https://milkdown.dev/docs/api/plugin-tooltip
Example of how to create a custom tooltip view by implementing the Prosemirror Plugin.view.
```typescript
import { TooltipProvider } from '@milkdown/kit/plugin/tooltip'
function tooltipPluginView(view) {
const content = document.createElement('div')
const provider = new TooltipProvider({
content: this.content,
})
return {
update: (updatedView, prevState) => {
provider.update(updatedView, prevState)
},
destroy: () => {
provider.destroy()
content.remove()
},
}
}
```
--------------------------------
### Basic Crepe Editor Setup
Source: https://milkdown.dev/docs/guide/getting-started
Initialize a basic Milkdown editor using the Crepe package. Ensure to import the necessary CSS for theming.
```typescript
import { Crepe } from '@milkdown/crepe'
import '@milkdown/crepe/theme/common/style.css'
import '@milkdown/crepe/theme/frame.css'
const crepe = new Crepe({
root: '#app',
defaultValue: 'Hello, Milkdown!',
})
crepe.create()
```
--------------------------------
### Install Milkdown Dependencies with npm
Source: https://milkdown.dev/docs/recipes/angular
Install the necessary Milkdown packages for Angular integration using npm.
```bash
npm install @milkdown/kit
npm install @milkdown/theme-nord
```
--------------------------------
### Install Collaborative Editing Plugins
Source: https://milkdown.dev/docs/guide/collaborative-editing
Install the necessary packages for collaborative editing, including the Milkdown collab plugin and Y.js.
```bash
npm install @milkdown/plugin-collab
npm install yjs y-protocols y-prosemirror
```
--------------------------------
### Advanced Crepe Editor Setup
Source: https://milkdown.dev/docs/guide/getting-started
Initialize a Milkdown editor with the Crepe package, including theme and default content. Demonstrates editor creation and destruction.
```typescript
import { Crepe } from '@milkdown/crepe'
import '@milkdown/crepe/theme/common/style.css'
/**
* Available themes:
* frame, crepe, nord
* frame-dark, crepe-dark, nord-dark
*/
import '@milkdown/crepe/theme/frame.css'
const crepe = new Crepe({
root: '#app',
defaultValue: 'Hello, Milkdown!',
})
crepe.create().then(() => {
console.log('Editor created')
})
// To destroy the editor
crepe.destroy()
```
--------------------------------
### Remark Plugin Setup
Source: https://milkdown.dev/docs/plugin/example-iframe-plugin
Import and configure the remark-directive plugin for custom markdown syntax support.
```typescript
import directive from 'remark-directive'
import { $remark } from '@milkdown/kit/utils'
const remarkDirective = $remark('remarkDirective', () => directive)
```
--------------------------------
### Install Milkdown Dependencies for NuxtJS
Source: https://milkdown.dev/docs/recipes/nuxtjs
Install the necessary Milkdown packages, including core, Vue integration, and a theme, using npm.
```bash
npm install @milkdown/vue
npm install @milkdown/kit
npm install @milkdown/theme-nord
```
--------------------------------
### Initialize Editor with Commonmark Preset
Source: https://milkdown.dev/docs/api/preset-commonmark
Basic setup for initializing the Milkdown editor with the commonmark preset. Ensure you have the necessary imports.
```typescript
import { Editor } from '@milkdown/kit/core'
import { commonmark } from '@milkdown/kit/preset/commonmark'
Editor.make().use(commonmark).create()
```
--------------------------------
### Anthropic Provider Configuration Example
Source: https://milkdown.dev/docs/api/crepe
Example of configuring the Anthropic provider, specifying `maxTokens`, `anthropicVersion`, and custom body fields. The `buildMessages` function for Anthropic returns an object with `system` and `messages` fields, as the system prompt is a top-level field.
```typescript
// Anthropic: `maxTokens` (default 4096), `anthropicVersion` (default
// '2023-06-01'), and any `/v1/messages` body fields via `body`.
// `buildMessages` returns `{ system, messages }` since Anthropic puts
// the system prompt in a top-level field rather than the messages array.
```
```typescript
createAnthropicProvider({
apiKey,
model: 'claude-sonnet-4-5',
maxTokens: 2048,
body: { temperature: 0.5 },
})
```
--------------------------------
### Setup Milkdown with Collab Plugin
Source: https://milkdown.dev/docs/api/plugin-collab
Demonstrates how to set up a Milkdown editor with the collab plugin and initialize Yjs for collaborative editing. Ensure you have a WebSocket host and room name configured.
```typescript
import { collab, collabServiceCtx } from '@milkdown/plugin-collab'
async function setup() {
const editor = await Editor.make().use(collab).create()
const doc = new Doc()
const wsProvider = new WebsocketProvider('', 'milkdown', doc)
editor.action((ctx) => {
const collabService = ctx.get(collabServiceCtx)
collabService
// bind doc and awareness
.bindDoc(doc)
.setAwareness(wsProvider.awareness)
// connect yjs with milkdown
.connect()
})
}
```
--------------------------------
### Sugar High Highlighter Setup
Source: https://milkdown.dev/docs/api/plugin-highlight
Create a Sugar High parser instance. No external dependencies are required for this parser.
```typescript
// For sugar high
import { createParser } from '@milkdown/plugin-highlight/sugar-high'
const parser = createParser()
```
--------------------------------
### Stack Open, Push, Close Example
Source: https://milkdown.dev/docs/api/transformer
Demonstrates how to use the Stack class to build a nested structure. It opens an element, pushes nodes into it, and then closes the element.
```typescript
stack.open(A).push(B).push(C).close()
```
--------------------------------
### @milkdown/plugin-listener Usage
Source: https://milkdown.dev/docs/api/plugin-listener
Example of how to import and use the listener plugin in a Milkdown editor configuration.
```APIDOC
## Usage
```typescript
import { Editor } from '@milkdown/kit/core'
import { listener, listenerCtx } from '@milkdown/kit/plugin/listener'
import { commonmark } from '@milkdown/kit/preset/commonmark'
import { nord } from '@milkdown/theme-nord'
Editor.make()
.config((ctx) => {
const listener = ctx.get(listenerCtx)
listener.markdownUpdated((ctx, markdown, prevMarkdown) => {
if (markdown !== prevMarkdown) {
YourMarkdownUpdater(markdown)
}
})
})
.use(listener)
// use other plugins
.create()
```
```
--------------------------------
### Accessing Editor Instance with useInstance Hook
Source: https://milkdown.dev/docs/recipes/react
Demonstrates how to use the `useInstance` hook within a `MilkdownProvider` to get the editor instance for advanced operations.
```tsx
import { useInstance } from '@milkdown/react'
import { getMarkdown } from '@milkdown/utils'
// ❌ This won't work - ParentComponent is outside MilkdownProvider
const ParentComponent: React.FC = () => {
const [isLoading, getInstance] = useInstance() // This will be [true, () => undefined]
return
}
// ✅ This is the correct way - EditorControls is inside MilkdownProvider
const EditorControls: React.FC = () => {
const [isLoading, getInstance] = useInstance()
const handleSave = () => {
if (isLoading) return
const editor = getInstance()
if (!editor) return
const content = editor.action(getMarkdown())
// Do something with the content
}
return (
)
}
// ✅ Proper component structure
const EditorWithControls: React.FC = () => {
return (
)
}
```
--------------------------------
### Milkdown Editor Setup with Highlight Plugin
Source: https://milkdown.dev/docs/api/plugin-highlight
Configure the Milkdown editor to use the highlight plugin. Set the parser in the plugin configuration.
```typescript
// Setup
import { highlight, highlightPluginConfig } from '@milkdown/plugin-highlight'
Editor.make()
.config((ctx) => {
ctx.set(highlightPluginConfig.key, { parser })
})
.use(highlight)
.create()
```
--------------------------------
### Bind Tooltip View to Editor
Source: https://milkdown.dev/docs/api/plugin-tooltip
Example of how to bind the created tooltip view to the Milkdown editor configuration.
```typescript
import { Editor } from '@milkdown/core'
import { tooltipFactory } from '@milkdown/plugin-tooltip'
const tooltip = tooltipFactory('my-tooltip')
Editor.make()
.config((ctx) => {
ctx.set(tooltip.key, {
view: tooltipPluginView,
})
})
.use(tooltip)
.create()
```
--------------------------------
### Insert Initial Content
Source: https://milkdown.dev/docs/guide/macros
Add initial content to the editor upon mounting using the `insert` macro within the listener plugin. This sets up the starting text for the user.
```typescript
import { insert } from '@milkdown/kit/utils'
import { listenerCtx } from '@milkdown/kit/plugin/listener'
editor.config((ctx) => {
ctx.get(listenerCtx).mounted(insert('# Welcome\nStart editing...'))
})
```
--------------------------------
### Sink List Item Command Example
Source: https://milkdown.dev/docs/api/preset-commonmark
Demonstrates the `sinkListItemCommand` which indents a list item. This is useful for creating nested lists.
```typescript
* List item 1
* List item 2 <-
Will get:
* List item 1
* List item 2
```
--------------------------------
### Custom Uploader Configuration
Source: https://milkdown.dev/docs/api/plugin-upload
Set up a custom uploader to handle file uploads and process the returned nodes. This example filters for image files and uses a placeholder `YourUploadAPI` function.
```typescript
import { upload, uploadConfig, Uploader } from '@milkdown/kit/plugin/upload'
import type { Node } from '@milkdown/kit/prose/model'
const uploader: Uploader = async (files, schema) => {
const images: File[] = []
for (let i = 0; i < files.length; i++) {
const file = files.item(i)
if (!file) {
continue
}
// You can handle whatever the file type you want, we handle image here.
if (!file.type.includes('image')) {
continue
}
images.push(file)
}
const nodes: Node[] = await Promise.all(
images.map(async (image) => {
const src = await YourUploadAPI(image) // Replace with your actual upload logic
const alt = image.name
return schema.nodes.image.createAndFill({
src,
alt,
}) as Node
})
)
return nodes
}
Editor.make()
.config((ctx) => {
ctx.set(uploadConfig, {
uploader
})
})
```
--------------------------------
### Initialize and Configure Crepe Editor
Source: https://milkdown.dev/docs/api/crepe
Demonstrates how to create a Crepe editor instance and add features with configurations.
```typescript
import { CrepeBuilder } from '@milkdown/crepe';
const editor = await new CrepeBuilder()
.addFeature(feature1, config1)
.addFeature(feature2)
.create();
```
--------------------------------
### Implement a Custom AI Provider
Source: https://milkdown.dev/docs/guide/ai-feature
Create a custom AI provider as an async generator that yields markdown chunks. This example uses `fetch` to communicate with an AI API.
```typescript
import type { AIProvider } from '@milkdown/crepe/feature/ai'
const customProvider: AIProvider = async function* (context, signal) {
const response = await fetch('/api/ai', {
method: 'POST',
body: JSON.stringify(context),
signal, // forward the abort signal so cancellation works
})
if (!response.ok || !response.body) throw new Error('AI request failed')
const reader = response.body.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done || signal.aborted) return
yield decoder.decode(value, { stream: true })
}
}
```
--------------------------------
### crepe.create()
Source: https://milkdown.dev/docs/guide/using-crepe
Initialize the Crepe editor instance.
```APIDOC
## crepe.create()
### Description
Initialize the editor.
### Usage
```javascript
await crepe.create()
```
```
--------------------------------
### Initialize Editor with Plugins
Source: https://milkdown.dev/docs/plugin/using-plugins
Demonstrates how to create a Milkdown editor instance and enable core plugins like commonmark, history, and listener.
```typescript
import { Editor } from '@milkdown/kit/core'
import { history } from '@milkdown/kit/plugin/history'
import { listener } from '@milkdown/kit/plugin/listener'
import { commonmark } from '@milkdown/kit/preset/commonmark'
Editor.make().use(commonmark).use(history).use(listener).create()
```
--------------------------------
### Mark Runner Example
Source: https://milkdown.dev/docs/api/transformer
Example of a `runner` function for a `MarkSerializerSpec`. It opens a new mark with the specified type and value.
```typescript
runner: (state, mark, node) => {
state.withMark(mark, 'emphasis');
}
```
--------------------------------
### Mark Matcher Example
Source: https://milkdown.dev/docs/api/transformer
Example of a `match` function for a `MarkSerializerSpec`. It checks if a mark's type name is 'emphasis'.
```typescript
match: (mark) => mark.type.name === 'emphasis'
```
--------------------------------
### Node Runner Example
Source: https://milkdown.dev/docs/api/transformer
Example of a `runner` function for a `NodeSerializerSpec`. It opens a node, processes its content, and closes the node.
```typescript
runner: (state, node) => {
state
.openNode(node.type.name)
.next(node.content)
.closeNode();
}
```
--------------------------------
### Node Matcher Example
Source: https://milkdown.dev/docs/api/transformer
Example of a `match` function for a `NodeSerializerSpec`. It checks if a node's type name is 'paragraph'.
```typescript
match: (node) => node.type.name === 'paragraph'
```
--------------------------------
### Install Milkdown Dependencies for Next.js
Source: https://milkdown.dev/docs/recipes/nextjs
Install the necessary Milkdown packages for React integration in your Next.js project using npm.
```bash
npm install @milkdown/react
npm install @milkdown/kit
npm install @milkdown/theme-nord
```
--------------------------------
### Rendered Code Block Example
Source: https://milkdown.dev/docs/guide/code-highlighting
An example of a JavaScript code block within the editor that will be rendered with syntax highlighting applied by the configured Shiki parser.
```javascript
console.log('Hello, world!')
const greeting = (name) => `Hello, ${name}!`
```
--------------------------------
### $useKeymap
Source: https://milkdown.dev/docs/api/utils
Creates a customizable keymap that allows users to define shortcuts and their associated commands.
```APIDOC
## $useKeymap (name: N, userKeymap: UserKeymapConfig) → $UserKeymap
### Description
Create a keymap which can be customized by user. It takes two arguments: `name` (the name of the keymap) and `userKeymap` (the keymap config).
### Parameters
* `name` (N): The name of the keymap.
* `userKeymap` (UserKeymapConfig): The keymap config which contains the shortcuts and the command.
### Returns
* `$UserKeymap`: The created user keymap plugin.
```
--------------------------------
### MarkParserSpec Match Example
Source: https://milkdown.dev/docs/api/transformer
Defines how to match a specific Markdown AST mark type for parsing.
```typescript
match: (mark) => mark.type === 'emphasis'
```
--------------------------------
### NodeParserSpec Match Example
Source: https://milkdown.dev/docs/api/transformer
Defines how to match a specific Markdown AST node type for parsing.
```typescript
match: (node) => node.type === 'paragraph'
```
--------------------------------
### Crepe Editor Initialization and Usage
Source: https://milkdown.dev/docs/api/crepe
Demonstrates how to initialize the Crepe editor with specified features and configurations, retrieve markdown content, set the editor to readonly mode, and listen for markdown update events.
```typescript
import { Crepe } from '@milkdown/crepe'
const editor = new Crepe({
root: '#editor', // DOM element or selector
features: {
[Crepe.Feature.Toolbar]: true,
[Crepe.Feature.Latex]: true,
},
featureConfigs: {
[Crepe.Feature.Placeholder]: {
text: 'Start writing...',
mode: 'block',
},
},
defaultValue: '# Hello World',
})
// Get markdown content
const markdown = editor.getMarkdown()
// Set readonly mode
editor.setReadonly(true)
// Listen to editor events
editor.on((listener) => {
listener.markdownUpdated((ctx, markdown, prevMarkdown) => {
// Handle updates
})
})
```
--------------------------------
### History Commands
Source: https://milkdown.dev/docs/api/plugin-history
Describes the programmatic commands for undo and redo and provides an example of how to execute them using `callCommand`.
```APIDOC
## Commands
### `undoCommand` : `$Command`
A Milkdown command wrapper for the undo API in prosemirror-history.
### `redoCommand` : `$Command`
A Milkdown command wrapper for the redo API in prosemirror-history.
### Programmatic Command Execution Example
```typescript
import { Undo, history } from '@milkdown/plugin-history'
import { callCommand } from '@milkdown/plugin-utils'
const editor = await Editor.make().use(/* ... */).use(history).create()
editor.action(callCommand(Undo))
```
```
--------------------------------
### Initialize Milkdown with Link Tooltip
Source: https://milkdown.dev/docs/api/component-link-tooltip
Configure and use the link tooltip plugin with the Milkdown editor. Ensure commonmark preset is included.
```typescript
import {
configureLinkTooltip,
linkTooltipPlugin,
linkTooltipConfig,
} from '@milkdown/components/link-tooltip'
import { defaultValueCtx, Editor } from '@milkdown/kit/core'
import { commonmark, linkSchema } from '@milkdown/kit/preset/commonmark'
const editor = await Editor.make()
.config(configureLinkTooltip)
.use(commonmark)
.use(linkTooltipPlugin)
.create()
```
--------------------------------
### Minimal Vanilla '/' Menu
Source: https://milkdown.dev/docs/plugin/example-slash-plugin
Creates a basic '/' menu in vanilla TypeScript that suggests two commands. The SlashProvider handles positioning and visibility, while the menu rendering and command execution are custom.
```typescript
import { SlashProvider, slashFactory } from '@milkdown/plugin-slash'
import { Editor } from '@milkdown/kit/core'
import { commonmark } from '@milkdown/kit/preset/commonmark'
// DOM content of the menu – plain HTML for the demo
const menu = document.createElement('div')
menu.className = 'slash-menu'
menu.style.cssText = `
position:absolute;padding:4px 0;background:white;border:1px solid #eee;
box-shadow:0 2px 8px rgba(0,0,0,.15);border-radius:6px;font-size:14px;
`
menu.innerHTML = `
Heading 1
Bullet List
`
// Click handler – replace with real commands
menu.addEventListener('click', (e) => {
const target = e.target as HTMLElement
const cmd = target.dataset.cmd
alert(`Run command: ${cmd}`)
})
// Provider positions & shows above DOM element
const provider = new SlashProvider({
content: menu,
// show the menu when the last character before caret is '/'
shouldShow(view) {
return provider.getContent(view)?.endsWith('/') ?? false
},
offset: 8,
})
const slash = slashFactory('demo')
const slashConfig = (ctx: Ctx) => {
ctx.set(slash.key, {
view: () => ({
update: provider.update,
destroy: provider.destroy,
}),
})
}
Editor.make().config(slashConfig).use(commonmark).use(slash).create()
```
--------------------------------
### Initializing the Editor
Source: https://milkdown.dev/docs/guide/using-crepe
Initialize the Crepe editor instance. This is a required step before using other editor functionalities.
```javascript
await crepe.create()
```
--------------------------------
### Initialize Upload Plugin
Source: https://milkdown.dev/docs/api/plugin-upload
Import and use the upload plugin with the Milkdown editor. No additional configuration is needed for basic image uploads.
```typescript
import { Editor } from '@milkdown/kit/core'
import { upload } from '@milkdown/kit/plugin/upload'
Editor.make().use(upload).create()
```
--------------------------------
### Get Markdown Content (Crepe)
Source: https://milkdown.dev/docs/guide/faq
Retrieve the current Markdown content from the editor when using the Crepe API.
```typescript
const markdown = editor.getMarkdown()
```
--------------------------------
### Initialize Editor with Table Block
Source: https://milkdown.dev/docs/api/component-table-block
Demonstrates how to import and use the tableBlock component when creating a Milkdown editor. Ensure commonmark and gfm presets are included if needed.
```typescript
import { tableBlock, tableBlockConfig } from '@milkdown/components/table-block'
import { Editor } from '@milkdown/kit/core'
import { commonmark } from '@milkdown/kit/preset/commonmark'
import { gfm } from '@milkdown/kit/preset/gfm'
await Editor.make().use(commonmark).use(gfm).use(tableBlock).create()
```
--------------------------------
### Timer Management
Source: https://milkdown.dev/docs/api/ctx
Manages timers, which are promises that can be resolved. Allows getting, removing, and checking for the existence of timers.
```APIDOC
## Class: Clock
### Description
Represents a map of timers.
### Methods
- **`get(timer: TimerType): Timer`**
Get a timer from the clock by timer type.
- **`remove(timer: TimerType): void`**
Remove a timer from the clock by timer type.
- **`has(timer: TimerType): boolean`**
Check if the clock has a timer by timer type.
## Class: TimerType
### Description
Used to create timers in different clocks.
### Constructors
- **`new TimerType(name: string, timeout?: number = 3000)`**
Create a timer type with a name and a timeout. The name should be unique in the clock.
### Properties
- **`id: Symbol`**
The unique id of the timer type.
- **`name: string`**
The name of the timer type.
- **`timeout: number`**
The timeout of the timer type.
### Methods
- **`create(clock: TimerMap): Timer`**
Create a timer with a clock.
## Class: Timer
### Description
A promise that can be resolved by calling done.
### Properties
- **`type: TimerType`**
The type of the timer.
- **`status: TimerStatus`**
The status of the timer. Can be `pending`, `resolved` or `rejected`.
### Methods
- **`start(): Promise`**
Start the timer, which will return a promise. If the timer is already started, it will return the same promise. If the timer is not resolved in the timeout, it will reject the promise.
- **`done(): void`**
Resolve the timer.
## Function: createTimer(name: string, timeout?: number = 3000): TimerType
### Description
Create a timer type with a name and a timeout. This is equivalent to `new TimerType(name, timeout)`.
```
--------------------------------
### Initialize CrepeBuilder with Features
Source: https://milkdown.dev/docs/api/crepe
Use CrepeBuilder to manually add features for optimized bundle size. Import necessary features and styles, then create the editor instance.
```typescript
import { CrepeBuilder } from '@milkdown/crepe/builder'
import { blockEdit } from '@milkdown/crepe/feature/block-edit'
import { toolbar } from '@milkdown/crepe/feature/toolbar'
import { topBar } from '@milkdown/crepe/feature/top-bar'
// You may also want to import styles by feature
import '@milkdown/crepe/theme/common/prosemirror.css'
import '@milkdown/crepe/theme/common/reset.css'
import '@milkdown/crepe/theme/common/block-edit.css'
import '@milkdown/crepe/theme/common/toolbar.css'
import '@milkdown/crepe/theme/common/top-bar.css'
// And introduce the theme
import '@milkdown/crepe/theme/crepe.css'
const builder = new CrepeBuilder({
root: '#editor',
defaultValue: '# Hello World',
})
// Add features manually
builder.addFeature(blockEdit).addFeature(toolbar).addFeature(topBar)
// Create the editor
const editor = await builder.create()
// Get markdown content
const markdown = builder.getMarkdown()
// Set readonly mode
builder.setReadonly(true)
// Listen to editor events
builder.on((listener) => {
listener.markdownUpdated((ctx, markdown, prevMarkdown) => {
// Handle updates
})
})
```
--------------------------------
### Keymap Structure Definition
Source: https://milkdown.dev/docs/guide/keyboard-shortcuts
Illustrates the structure for defining a keymap, including command name, shortcuts, optional priority, and the command to execute.
```javascript
$useKeymap('keymapName', {
CommandName: {
shortcuts: string | string[], // Single shortcut or array of shortcuts
priority?: number, // (Optional) Priority of the shortcut
command: (ctx) => () => { // Command to execute
const commands = ctx.get(commandsCtx);
return () => commands.call(commandKey, ...args);
},
},
});
```
--------------------------------
### MarkParserSpec Runner Example
Source: https://milkdown.dev/docs/api/transformer
Specifies the logic for transforming a matched Markdown AST mark into a Prosemirror mark using the ParserState.
```typescript
runner: (state, node, type) => {
state
.openMark(type)
.next(node.children)
.closeMark(type)
}
```
--------------------------------
### Initialize Milkdown Editor with Cursor Plugin
Source: https://milkdown.dev/docs/api/plugin-cursor
This snippet shows how to import and use the cursor plugin when creating a Milkdown editor. Ensure necessary presets and themes are also included.
```typescript
import { Editor } from '@milkdown/kit/core'
import { cursor } from '@milkdown/kit/plugin/cursor'
import { commonmark } from '@milkdown/kit/preset/commonmark'
import { nord } from '@milkdown/theme-nord'
Editor.make().use(nord).use(commonmark).use(cursor).create()
```
--------------------------------
### NodeParserSpec Runner Example
Source: https://milkdown.dev/docs/api/transformer
Specifies the logic for transforming a matched Markdown AST node into a Prosemirror node using the ParserState.
```typescript
runner: (state, node, type) => {
state
.openNode(type)
.next(node.children)
.closeNode();
}
```
--------------------------------
### Initialize Editor with Image Block Component
Source: https://milkdown.dev/docs/api/component-image-block
Import and use the `imageBlockComponent` along with `commonmark` preset to enable image block functionality in the editor.
```typescript
import {
imageBlockComponent,
imageBlockConfig,
} from '@milkdown/components/image-block'
import { defaultValueCtx, Editor } from '@milkdown/kit/core'
import { commonmark } from '@milkdown/kit/preset/commonmark'
await Editor.make().use(commonmark).use(imageBlockComponent).create()
```
--------------------------------
### Get Document Outline
Source: https://milkdown.dev/docs/guide/macros
Retrieve the document's outline using the `outline` macro. This is useful for understanding the structure of the content.
```typescript
import { outline } from '@milkdown/kit/utils'
const docOutline = editor.action(outline())
```
--------------------------------
### Initialize Crepe Editor
Source: https://milkdown.dev/docs/guide/interacting-with-editor
Create a new Crepe editor instance, optionally specifying the root element and default content. The editor must be created asynchronously.
```typescript
import { Crepe } from '@milkdown/crepe'
// Create a new editor instance
const editor = new Crepe({
// Optional: specify root element (DOM node or selector)
root: '#editor',
// Optional: set default content, supports markdown, json and dom.
defaultValue: '# Hello Crepe!',
})
// Create the editor
await editor.create()
// Get markdown content
const markdown = editor.getMarkdown()
// Set readonly mode
editor.setReadonly(true)
// Register event listeners
editor.on((listener) => {
listener.markdownUpdated((ctx, markdown, prevMarkdown) => {
console.log('Content updated:', markdown)
})
listener.focus((ctx) => {
console.log('Editor focused')
})
listener.blur((ctx) => {
console.log('Editor blurred')
})
listener.selectionUpdated((ctx, selection, prevSelection) => {
console.log('Selection updated:', selection)
```
--------------------------------
### Get Markdown Content (Kit API)
Source: https://milkdown.dev/docs/guide/faq
Retrieve the current Markdown content from the editor using the Kit API and `serializerCtx`.
```typescript
import { editorViewCtx, serializerCtx } from '@milkdown/kit/core'
const getMarkdown = (editor) =>
editor.action((ctx) => {
const serializer = ctx.get(serializerCtx)
const view = ctx.get(editorViewCtx)
return serializer(view.state.doc)
})
```
--------------------------------
### Slice Management
Source: https://milkdown.dev/docs/api/ctx
Manages slices, which are typed values within a container. Allows getting, removing, and checking for the existence of slices.
```APIDOC
## Class: Container
### Description
Represents a map of slices.
### Methods
- **`get(slice: SliceType | N): Slice`**
Get a slice from the container by slice type or slice name.
- **`remove(slice: SliceType | N): void`**
Remove a slice from the container by slice type or slice name.
- **`has(slice: SliceType | N): boolean`**
Check if the container has a slice by slice type or slice name.
## Class: SliceType
### Description
Used to create slices in different containers.
### Constructors
- **`new SliceType(value: T, name: N)`**
Create a slice type with a default value and a name. The name should be unique in the container.
### Properties
- **`id: Symbol`**
The unique id of the slice type.
- **`name: N`**
The name of the slice type.
### Methods
- **`create(container: SliceMap, value?: T = this._defaultValue): Slice`**
Create a slice with a container. You can also pass a value to override the default value.
## Class: Slice
### Description
Represents a value of a slice type.
### Properties
- **`type: SliceType`**
The type of the slice.
### Methods
- **`on(watcher: fn(value: T) => unknown): fn()`**
Add a watcher for changes in the slice. Returns a function to remove the watcher.
- **`once(watcher: fn(value: T) => unknown): fn()`**
Add a one-time watcher for changes in the slice. The watcher will be removed after it is called. Returns a function to remove the watcher.
- **`off(watcher: fn(value: T) => unknown): void`**
Remove a watcher.
- **`offAll(): void`**
Remove all watchers.
- **`set(value: T): void`**
Set the value of the slice.
- **`get(): T`**
Get the value of the slice.
- **`update(updater: fn(prev: T) => T): void`**
Update the value of the slice with a callback.
## Function: createSlice(value: T, name: N): SliceType
### Description
Create a slice type with a default value and a name. This is equivalent to `new SliceType(value, name)`.
```
--------------------------------
### Basic Upload Plugin Usage
Source: https://milkdown.dev/docs/api/plugin-upload
Demonstrates the basic integration of the upload plugin into a Milkdown editor.
```APIDOC
## Basic Upload Plugin Usage
### Description
This snippet shows how to import and use the upload plugin in a Milkdown editor.
### Method
Editor Configuration
### Endpoint
N/A
### Parameters
N/A
### Request Example
```typescript
import { Editor } from '@milkdown/kit/core'
import { upload } from '@milkdown/kit/plugin/upload'
Editor.make().use(upload).create()
```
### Response
N/A
```
--------------------------------
### Create an Async Shortcut Plugin
Source: https://milkdown.dev/docs/api/utils
Use $shortcutAsync for the asynchronous version of $shortcut. You can use `await` in the factory function when creating the keymap. The timer will resolve when the plugin is ready.
```typescript
const asyncShortcutPlugin = $shortcutAsync(async (ctx) => await keymap);
```
--------------------------------
### Configuring Cursor Feature in Crepe
Source: https://milkdown.dev/docs/api/crepe
Example of configuring the Cursor feature, allowing customization of color, width, and virtual cursor behavior.
```typescript
const config: CrepeConfig = {
features: {
[Crepe.Feature.Cursor]: true,
},
featureConfigs: {
[Crepe.Feature.Cursor]: {
color: '#ff0000',
width: 2,
virtual: true,
},
},
}
```
--------------------------------
### Working with Transactions in Milkdown
Source: https://milkdown.dev/docs/guide/prosemirror-api
Manipulate the editor's state by creating and dispatching transactions. This example demonstrates inserting text into the editor.
```typescript
editor.action((ctx) => {
const view = ctx.get(editorViewCtx)
const { tr } = view.state
// Apply transformations
view.dispatch(tr.insertText('Hello'))
})
```
--------------------------------
### Trailing Plugin Usage
Source: https://milkdown.dev/docs/api/plugin-trailing
Demonstrates how to import and use the trailing plugin with the Milkdown editor.
```APIDOC
## Import and Use Trailing Plugin
### Description
This snippet shows the basic import and usage of the `trailing` plugin within a Milkdown editor instance.
### Method
```typescript
import { Editor } from '@milkdown/kit/core'
import { trailing } from '@milkdown/kit/plugin/trailing'
Editor.make().use(trailing).create()
```
### Plugin
#### `trailing` : `MilkdownPlugin[]`
All plugins exported by this package.
```
--------------------------------
### Initialize Milkdown with Clipboard Plugin
Source: https://milkdown.dev/docs/api/plugin-clipboard
Use this snippet to initialize the Milkdown editor with the clipboard plugin enabled. Ensure the plugin is imported correctly.
```typescript
import { Editor } from '@milkdown/kit/core'
import { clipboard } from '@milkdown/kit/plugin/clipboard'
Editor.make().use(clipboard).create()
```
--------------------------------
### Configure CodeMirror Languages and Theme
Source: https://milkdown.dev/docs/api/crepe
Set a custom theme and specify available languages for the CodeMirror feature. This example loads only the Markdown language.
```typescript
import { oneDark } from '@codemirror/theme-one-dark'
import { LanguageDescription } from '@codemirror/language'
import { markdown } from '@codemirror/lang-markdown'
const config: CrepeConfig = {
features: {
[Crepe.Feature.CodeMirror]: true,
},
featureConfigs: {
[Crepe.Feature.CodeMirror]: {
theme: oneDark,
languages: [
// Only load markdown language
LanguageDescription.of({
name: 'Markdown',
extensions: ['md', 'markdown'],
load() {
return import('@codemirror/lang-markdown').then((m) => m.markdown())
},
}),
],
},
},
}
```
--------------------------------
### Initialize Crepe with OpenAI Provider
Source: https://milkdown.dev/docs/guide/ai-feature
This snippet shows how to initialize the Crepe editor with the AI feature enabled and configured to use OpenAI's Chat Completions API via a custom backend proxy. Ensure the AI feature is explicitly enabled and a provider is supplied.
```typescript
import { Crepe, CrepeFeature } from '@milkdown/crepe'
import { createOpenAIProvider } from '@milkdown/crepe/llm-providers/openai'
import '@milkdown/crepe/theme/common/style.css'
import '@milkdown/crepe/theme/frame.css'
const crepe = new Crepe({
root: '#app',
features: {
[CrepeFeature.AI]: true,
},
featureConfigs: {
[CrepeFeature.AI]: {
provider: createOpenAIProvider({
// Route through your backend so the key never reaches the browser.
baseURL: 'https://your-app.example.com/openai-proxy',
model: 'gpt-4o-mini',
}),
},
},
})
await crepe.create()
```
--------------------------------
### Accessing Crepe Editor Instance in Plugin
Source: https://milkdown.dev/docs/api/crepe
Shows how to get the Crepe editor instance within a Milkdown plugin using the useCrepe utility.
```typescript
import { Ctx } from '@milkdown/core';
import { crepeCtx, useCrepe } from '@milkdown/crepe';
const plugin = (ctx: Ctx) => {
return () => {
const crepe = useCrepe(ctx);
crepe.setReadonly(true);
};
}
```