Files
llm-in-text/milkdown-docs/plugin/example-slash-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
Raw Permalink Blame History

Example: Slash Plugin

After reading the tooltip guide you already know how Milkdown separates positioning logic (provider) from editor wiring (ctx slices produced by a factory). The @milkdown/plugin-slash package applies exactly the same idea but focuses on command palettes triggered by a character familiar to / menus in modern editors.

This document shows you how to:

  • Understand what the slash plugin gives you out-of-the-box.
  • Build a vanilla TypeScript implementation of a basic / menu.
  • Use the slash provider with React and Vue.
  • Explore a full-blown menu feature that ships inside Milkdown's Crepe UI.

1. Anatomy of a Slash Plugin

@milkdown/plugin-slash exports two utilities:

  1. SlashProvider Measures the caret position and manages show / hide of your menu.
  2. slashFactory(id) Generates a ctx slice & ProseMirror plugin pair that plugs the provider into the editor.
import { slashFactory } from "@milkdown/plugin-slash";

export const [mySlashSpec, mySlashPlugin] = slashFactory("my");

Just like the tooltip factory:

  • mySlashSpec is where you put a PluginSpec (what ProseMirror needs).
  • mySlashPlugin turns that spec into a runtime plugin.

2. A Minimal Vanilla / Menu

Below we create a small menu that suggests two commands whenever the user types /.

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 = `<ul style="margin:0;padding:0;list-style:none">
  <li data-cmd="h1" style="padding:4px 12px;cursor:pointer">Heading 1</li>
  <li data-cmd="bullet" style="padding:4px 12px;cursor:pointer">Bullet List</li>
</ul>`;

// 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();

Key takeaways:

  • SlashProvider has a helper getContent(view) to fetch text before the caret handy for filtering.
  • You decide when to show the menu via the shouldShow callback (default: when last char is /).
  • The provider only manipulates position + visibility; rendering & commands are completely yours.

3. Framework Examples

React

::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/react-slash"}

Highlights:

  1. A <SlashMenu/> React component renders the list.
  2. The component root is passed to SlashProvider (just like the tooltip demo).
  3. React hooks manage internal focus & keyboard navigation.

Vue

::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/vue-slash"}

The Vue version uses Teleport to append the menu to document.body and ref / watch for reactivity.


4. Real-world Feature Crepe Block Menu

Milkdown's Crepe UI implements an extensible block-level menu on top of the slash plugin. You'll find the source code at:

packages/crepe/src/feature/block-edit/menu/

Notable patterns to look for:

  • Context slices (menu / menuAPI) to expose imperative show & hide methods.
  • Filtering commands based on the current text after /.
  • Preventing the menu inside code blocks or lists.

Studying this folder is a great next step once you master the basics.


5. Summary & Next Steps

  • @milkdown/plugin-slash gives you caret detection + positioning nothing else.
  • UI, behaviour, and commands are fully customisable.

Fork one of the examples above, add your own commands, and you'll have a modern / command palette in minutes .