An original inspection-log editor offers “Section heading” when an author types /section at the start of a line. Choosing it should remove the entire trigger query and turn that paragraph into a heading. The saved log should contain the heading, without /section left in its text.
Define a small command catalog
Start with a command that the installed schema already supports. This application-level item assumes Heading is enabled with level 2:
const logCommands = [{
id: 'section-heading',
title: 'Section heading',
execute: ({ editor, range }) => {
editor.chain()
.focus()
.deleteRange(range)
.setNode('heading', { level: 2 })
.run();
}
}];
range must come from the currently active suggestion. It includes the matched slash text. Deleting a hard-coded number of characters or using an old selection can leave part of the query behind or remove neighboring prose.
The command creates document structure. It does not insert a permanent “command” node or a person identifier. That distinction matters when adapting an existing mention integration: a menu can look similar while producing a different saved document.
Configure matching inside a custom extension
The following fragment supplies the Suggestion options for your extension's addProseMirrorPlugins() implementation:
import Suggestion from '@tiptap/suggestion';
import { PluginKey } from '@tiptap/pm/state';
function logCommandPlugin(editor, renderCommandMenu) {
return Suggestion({
editor,
pluginKey: new PluginKey('inspectionCommands'),
char: '/',
startOfLine: true,
items: ({ query }) => logCommands.filter(item =>
item.title.toLowerCase().includes(query.toLowerCase())
),
command: ({ editor, range, props }) => {
props.execute({ editor, range });
},
render: renderCommandMenu
});
}
This is a plugin factory, not a complete editor or popup implementation. Your extension returns the resulting plugin from addProseMirrorPlugins(). Match the packages to the installed Tiptap version. The Suggestion API documents matching, command dispatch, and rendering lifecycle options.
startOfLine: true deliberately keeps this log's slash menu out of ordinary mid-sentence text and URLs. Add an allow rule if the application's command must also be restricted by node type or command availability. Test it inside code blocks and other restricted content rather than assuming every context supports a heading conversion.
Supply the menu behavior that the factory leaves open
renderCommandMenu is your implementation. It must show current items, keep a valid active item as results change, and call the latest suggestion props.command(chosenItem) when an author chooses a result. It also needs keyboard navigation, an empty-results state, dismissal, and cleanup through the documented lifecycle.
A menu that displays filtered labels but never calls the command is only a search interface. A menu that calls an item captured before the query changed can apply the wrong operation. Render labels as text and make selection and focus visible. Use the current Suggestion positioning lifecycle for scrolling and resizing; a one-time coordinate calculation can leave the popup detached from its slash.
Verify the document result and normal slash typing
Use a fictional log with the sentence “Inspect the drain tray” followed by an empty paragraph. Type /section in the empty paragraph, choose Section heading, and enter “Drain observations.” Inspect the document model: the new text should be a level-2 heading and the trigger text should be gone.
Undo the choice and check the editor's actual transaction result. Also try an unmatched query, Escape, and a slash in an ordinary sentence. None should silently insert the first available command. Save and reopen the log, then read the heading and the preceding instruction together.
An empty-node placeholder can explain how to begin, but the hint must remain separate from saved log content. Ship the command only when its typed trigger, selection interface, and resulting heading all agree in the application your authors use.