Files
llm-in-text/milkdown-docs/plugin/example-marker-plugin.md
ydy0615 d9ab341223 Add documentation for using Milkdown with various frameworks
- Created a new document for using components in Milkdown.
- Added a guide for using plugins in Milkdown, including toggling plugins programmatically and listing official plugins.
- Introduced a recipe for integrating Milkdown with Angular, including installation steps and component creation.
- Added a recipe for using Milkdown with Next.js, detailing installation and component setup.
- Created a guide for integrating Milkdown with NuxtJS, including installation and component creation.
- Added a comprehensive guide for using Milkdown with React, covering both Crepe and core Milkdown usage.
- Introduced a recipe for SolidJS integration with Milkdown, including installation and component creation.
- Added a guide for using Milkdown with Svelte, detailing installation and component setup.
- Created a comprehensive guide for integrating Milkdown with Vue, covering both Crepe and core Milkdown usage.
- Added a recipe for using Milkdown with Vue2, including installation and component creation.
2026-01-17 14:18:08 +08:00

4.4 KiB

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:

==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 to make it works correctly.

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:

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:

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:

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:

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"}