Files
llm-in-text/milkdown-docs/plugin/example-marker-plugin.md

190 lines
4.4 KiB
Markdown
Raw Permalink Normal View History

# Example: Marker Plugin
This guide demonstrates how to create a custom marker syntax plugin for Milkdown. This plugin allows you to mark text with custom colors using a simple markdown syntax.
## Overview
---
The marker plugin enables you to mark text using the following syntax:
```markdown
==marked text==
=={#EE4B2B}marked text with color==
```
This will render as marked text in your document, with the option to specify custom colors.
## Implementation Steps
---
To create a custom marker syntax plugin in Milkdown, we need to implement several components:
1. **Remark Plugin**: Parse the custom syntax
2. **Schema Definition**: Define the mark structure
3. **Parser**: Convert markdown to ProseMirror marks
4. **Serializer**: Convert ProseMirror marks back to markdown
5. **Input Rules**: Handle user input
6. **Color Picker**: Add UI for color selection
Let's implement each component:
## 1. Remark Plugin
---
First, we create a remark plugin to handle our custom marker syntax:
> ⚠️ The real implementation is more complex, but we simplify it for the sake of the example.
> Under the hood, you'll need to write a [micromark extension](https://github.com/micromark/micromark) to make it works correctly.
```typescript
import { $remark } from "@milkdown/kit/utils";
const remarkMarkColor = () => {
return (tree: any) => {
visit(tree, "text", (node: any, index: number, parent: any) => {
const match = node.value.match(/==(?:{#([^}]+)})?([^=]+)==/);
if (match) {
const [_, color, text] = match;
const mark = {
type: "mark",
data: { color },
children: [{ type: "text", value: text }],
};
parent.children.splice(index, 1, mark);
}
});
};
};
const milkdownMarkColorPlugin = $remark("markColor", () => remarkMarkColor);
```
## 2. Schema Definition
---
Next, we define the schema for our marker:
```typescript
import { $markSchema } from "@milkdown/kit/utils";
import { Mark } from "mdast";
export const DEFAULT_COLOR = "#ffff00";
export const markSchema = $markSchema("mark", () => ({
attrs: {
color: {
default: DEFAULT_COLOR,
validate: "string",
},
},
parseDOM: [
{
tag: "mark",
getAttrs: (node: HTMLElement) => ({
color: node.style.backgroundColor,
}),
},
],
toDOM: (mark) => ["mark", { style: `background-color: ${mark.attrs.color}` }],
parseMarkdown: {
match: (node) => node.type === "mark",
runner: (state, node, markType) => {
const color = (node as Mark).data?.color;
state.openMark(markType, { color });
state.next(node.children);
state.closeMark(markType);
},
},
toMarkdown: {
match: (node) => node.type.name === "mark",
runner: (state, mark) => {
let color = mark.attrs.color;
if (color?.toLowerCase() === DEFAULT_COLOR.toLowerCase()) {
color = undefined;
}
state.withMark(mark, "mark", undefined, {
data: { color },
});
},
},
}));
```
## 3. Input Rules
---
We add input rules to handle user typing:
```typescript
import { $inputRule } from "@milkdown/kit/utils";
import { InputRule } from "@milkdown/kit/prose";
const markInputRule = $inputRule(
() =>
new InputRule(/==(?:{#([^}]+)})?([^=]+)==/, (state, match, start, end) => {
const [okay, color, text] = match;
const { tr } = state;
if (okay) {
tr.addMark(
start,
end,
markSchema.type().create({ color: color || DEFAULT_COLOR }),
);
}
return tr;
}),
);
```
## 4. Color Picker Tooltip
---
To enhance the user experience, we add a color picker tooltip:
```typescript
export const colorPickerTooltip = tooltipFactory("color-picker");
class TooltipPluginView {
// ... implementation
}
export const colorPickerTooltipConfig = (ctx: Ctx) => {
ctx.set(colorPickerTooltip.key, {
view: () => new TooltipPluginView(ctx),
});
};
```
## Usage
---
To use the marker plugin, add it to your Milkdown editor configuration:
```typescript
import { Editor } from "@milkdown/kit/core";
import { commonmark } from "@milkdown/kit/preset/commonmark";
Editor.make()
.use(milkdownMarkColorPlugin)
.use(markSchema)
.use(markInputRule)
.use(colorPickerTooltip)
.use(commonmark)
.create();
```
## Example
---
Here's a complete example of the marker plugin in action:
::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/vanilla-highlight-syntax"}