Extension framework changes for Editor 2.0 (New Editor)
This document covers all APIs added to guides.editor (and guides) as part of the extension framework for the New Editor (ProseMirror-based editor). These APIs allow external extensions to interact with the editor without direct DOM manipulation or internal implementation knowledge.
Overview
The global guides object is the entry point for all extension integrations:
guides.editor // Editor interaction APIs
guides.util // Utility libraries (lodash, async)
guides.ready(cb) // Lifecycle hook fires when the editor is fully loaded
All guides.editor APIs are safe to call from extension controllers, toolbar handlers, and dialog logic.
Lifecycle
guides.ready(callback): Registers a callback to execute once the page view manager has fully loaded. Use this before registering plugins or performing any editor setup.
Signature:
guides.ready(callback: () => void): void
Example:
guides.ready(() => {
// Safe to access guides.editor APIs here
guides.editor.registerPlugin(createMyPlugin);
});
Editor state properties
-
guides.editor.filePath: Returns the file path of the currently active document.Type:
string | undefinedExample: reading file path for a metadata API call:
code language-js const filePath = guides.editor.filePath; if (filePath) { tcx.api.getMetadata(filePath).subscribe((metadata) => { // use metadata... }); }Example: conditional logic based on file location
code language-js const filePath = guides.editor.filePath; if (filePath?.endsWith(".ditamap")) { // ditamap-specific handling } -
guides.editor.version: Returns the version string of the currently loaded editor.table 0-row-2 1-row-2 2-row-2 Value Editor 1.0.0Legacy CKEditor ( xml_author_view)2.0.0New Editor (ProseMirror-based) Type:
stringExample:
code language-js if (guides.editor.version === '1.0.0') { // CKEditor specific logic } -
guides.editor.appState(keyName): Reads a value from the application model by key name.Signature:
code language-ts guides.editor.appState(keyName: string): unknownExample:
code language-js const editorMode = guides.editor.appState('editorMode');
Editor interaction
-
guides.editor.focus(): Sets focus to the active editor. Call this before executing commands that require the editor to have focus.Signature:
code language-ts guides.editor.focus(): booleanExample: focus before inserting content
code language-js guides.editor.focus(); const hasSelection = !!guides.editor.runUtil('hasSelection'); // ...proceed with wrap or insert -
guides.editor.canInsertXmlElement(tagName, insertAfter?): Checks whether a given XML element can be inserted at the current cursor position.Signature:
code language-ts guides.editor.canInsertXmlElement(tagName: string, insertAfter?: boolean): booleanExample: guard before inserting
<xref>code language-js const canInsert = guides.editor.canInsertXmlElement("xref"); if (!canInsert) { return tcx.util.showAlert("warning", "xref is not allowed here"); }Example: guard before inserting
<sup>/<sub>code language-js const canInsert = guides.editor.canInsertXmlElement("sup"); if (!canInsert) { return tcx.util.showAlert("warning", "superscript is not allowed here"); } -
guides.editor.selectCurrentBlockElement(): Selects the current block-level element in the editor.Signature:
code language-ts guides.editor.selectCurrentBlockElement(): booleanExample:
code language-js guides.editor.selectCurrentBlockElement(); -
guides.editor.getValidElementNamesForInsertion(args): Returns a list of valid XML element names that can be inserted at the current position.Signature:
code language-ts guides.editor.getValidElementNamesForInsertion(args: { insertMode?: 'after' | 'before' | 'rename', strict: boolean }): string[] | falseExample:
code language-js const validElements = guides.editor.getValidElementNamesForInsertion({ insertMode: 'after', strict: true }); -
guides.editor.updateAttributeByXpath(args): Updates a specific XML attribute on a node identified by its XPath. Delegates to the active editor’supdateAttributeByXpathmethod.Signature:
code language-ts guides.editor.updateAttributeByXpath(args: UpdateXpathArgs): any
Command execution
-
guides.editor.runCommand(commandName, ...args): Executes a named command on the New Editor. Returnstrueif the command succeeded,falseotherwise.Signature:
code language-ts guides.editor.runCommand(commandName: string, ...args: any[]): booleanAvailable commands
note NOTE Stability: The commands listed below are part of the public interface. Their names, signatures, and observable behavior are considered stable and are maintained for compatibility. Additional commands may be present within the editor; however, these are internal, are not intended for external use, and may change or be removed without prior notice. table 0-row-4 1-row-4 2-row-4 3-row-4 4-row-4 5-row-4 6-row-4 7-row-4 8-row-4 9-row-4 10-row-4 11-row-4 12-row-4 13-row-4 14-row-4 15-row-4 16-row-4 17-row-4 18-row-4 19-row-4 20-row-4 21-row-4 22-row-4 23-row-4 24-row-4 25-row-4 26-row-4 27-row-4 28-row-4 29-row-4 30-row-4 31-row-4 32-row-4 Category Command Arguments Description General undo(none) Undoes the last document change General redo(none) Redoes the last undone change Selection selectfrom: number, to?: numberSelects the range from fromtoto(or justfromiftois omitted)Selection cursorpos: numberMoves the cursor to posSelection selectNodesFromXpathsxpaths: Array<{path: Array<{name: string, count: number}>}>Selects nodes identified by XPath positions Formatting toggleBold(none) Toggles bold formatting on the current selection Formatting toggleItalic(none) Toggles italic formatting on the current selection Formatting toggleUnderline(none) Toggles underline formatting on the current selection Formatting toggleFormattingmarkType: string, allowEmptySelection?: booleanToggles a formatting element (e.g., 'b','i','sup','sub') on the selectionInsertion insertTexttext: stringInserts plain text at the cursor Insertion insertXmlxml: string, position?: number, options?: InsertXmlOptionsInserts raw XML at the cursor or given position Insertion insertXmlAfterElementelementName: string, xmlString: stringInserts XML immediately after the nearest ancestor matching elementNameInsertion replaceSelectionWithXmlxmlString: stringReplaces the current selection with the given XML Node Operations surroundWithElementtagName: string, attrs?: Record<string,unknown>, replaceTextWithEmptyNode?: booleanWraps the current selection with the given element Node Operations wrapNodetype: string, target?: number, attrs?: Record<string, unknown>Wraps the node at target(or the current node) in an element oftypeNode Operations renameNodenewNodeName: stringRenames the current node’s element Node Operations unwrapNode(none) Removes the current node’s wrapper element, keeping its content Delete deleteSelection(none) Deletes the currently selected content Delete deleteNode(none) Deletes the entire current node Attributes setNodeXmlAttributesposition: number, attrs: Record<string, unknown>Sets multiple XML attributes on the node at positionAttributes setNodeXmlAttributeposition: number, attrName: string, value: stringSets a single XML attribute on the node at positionLists toggleListlistName: 'ol' | 'ul'Toggles the current block between a list of the given type and a paragraph Tables addRowAftercount?: numberAdds countrows below the current row (default 1)Tables addColumnAftercount?: numberAdds countcolumns to the right of the current column (default 1)Tables deleteRow(none) Deletes the current row Tables deleteColumn(none) Deletes the current column Tables mergeCells(none) Merges the currently selected cells Clipboard copy(none) Copies the current selection to the clipboard Clipboard cut(none) Cuts the current selection to the clipboard Find & Replace setSearchQueryquery: string, options?: { caseSensitive?: boolean, regex?: boolean }Sets the active search query Find & Replace findNext(none) Moves to the next search match Find & Replace replaceAllreplacement?: stringReplaces every match of the current search query with replacement-
Example: Set multiple attributes on a node
code language-js guides.editor.runCommand( "setNodeXmlAttributes", rootRange.from, { createdDate: "2024-01-01", author: "Jane Doe" } ); -
Example: Set a single attribute on a node
code language-js guides.editor.runCommand( "setNodeXmlAttribute", range.from, "placeholdertext", "Chapter 3 — Safety Requirements" ); -
Example: Wrap selection with an element and set attributes
code language-js const didWrap = guides.editor.runCommand( "surroundWithElement", "ph", { outputclass: "highlight" }, true // replace text content with empty node ); -
Example: Wrap selection in
<sup>(toggle superscript on)code language-js const didWrap = guides.editor.runCommand('surroundWithElement', 'sup'); if (!didWrap) { tcx.util.showAlert("warning", "superscript is not allowed here"); } -
Example: Unwrap current node (toggle superscript off)
code language-js const didUnwrap = guides.editor.runCommand('unwrapNode'); -
Example: Insert XML at cursor with caret placed inside
code language-js guides.editor.runCommand( 'insertXml', '<sup></sup>', undefined, { setCursorInContent: true, focusEditor: true, selectInsertedXml: false } );
-
-
guides.editor.canRunCommand(commandName, ...args): Checks whether a named command can currently be executed, without actually running it.Signature:
code language-ts guides.editor.canRunCommand(commandName: string, ...args: any[]): booleanExample:
code language-js if (guides.editor.canRunCommand('surroundWithElement', 'sup')) { guides.editor.runCommand('surroundWithElement', 'sup'); }
Utility functions
-
guides.editor.runUtil(utilName, ...args): Invokes a named utility from the New Editor’s utility registry. Returns the utility’s result, orundefinedif the utility is not found.Signature:
code language-ts guides.editor.runUtil(utilName: string, ...args: any[]): anyAvailable utilities
note NOTE Stability: The utilities listed below are part of the public interface. Their names, signatures, and observable behavior are considered stable and are maintained for compatibility. Additional commands may be present within the editor; however, these are internal, are not intended for external use, and may change or be removed without prior notice. table 0-row-5 1-row-5 2-row-5 3-row-5 4-row-5 5-row-5 6-row-5 7-row-5 8-row-5 9-row-5 10-row-5 11-row-5 12-row-5 13-row-5 14-row-5 15-row-5 16-row-5 17-row-5 18-row-5 19-row-5 20-row-5 21-row-5 22-row-5 23-row-5 24-row-5 25-row-5 26-row-5 27-row-5 28-row-5 29-row-5 30-row-5 Category Utility Arguments Returns Description Position getTextPos(none) { start: number, end: number }Start and end positions of the current selection in the ProseMirror document Position getNodePositionposition?: numbernumberDocument position of the currently selected/focused node Position mapToXpathposition: numberXPathPositionMaps a ProseMirror position to an XPath descriptor Position inverseMapxpath: XPathPosition | numbernumberMaps an XPath descriptor (or numeric position) back to a ProseMirror position Document getNodeTree(none) NodeTree | nullTree representation of the document, suitable for outline rendering Document getAncestorsNamesposition?: numberstring[]XML tag names of ancestor nodes at positionDocument getAncestorsDetailsposition?: number{ currNode: string, ancestors: Array<{tagName: string}>, previousSibling?: string, nextSibling?: string } | undefinedCurrent node, ancestors, and immediate siblings at positionDocument getAncestorXpathsincludeId?: booleanAncestorXpathItem[]XPath strings for all ancestor nodes Document getPreviousSiblingposition?: numberstring | undefinedTag name of the previous sibling, if any Document getNextSiblingposition?: numberstring | undefinedTag name of the next sibling, if any Document getTagNameposition?: numberstring | nullTag name of the element at position(or cursor)Element Search findPositionRangetagName: string{ from: number, to: number } | undefinedRange of the first element with the given tag Element Search findPositionRangestagName: stringArray<{ from: number, to: number }>All ranges for elements matching tagNameAttributes getAttributeAtPositionposition: number, attrName: stringunknownReads an XML attribute value from the node at positionAttributes getSerializableAttributesxpath: stringRecord<string, unknown>All serializable XML attributes for the node at xpathSerialization serialize(none) stringSerializes the entire document to XML Serialization serializeToText(none) stringSerializes the entire document to plain text Serialization getRangeXmlxpaths: AncestorXpathItem[]stringReturns the XML for an XPath-defined range Selection getSelectedXml(none) stringCurrently selected content as an XML string Selection getSelectedText(none) stringSelected text without any markup Selection hasSelection(none) booleantrueif there is an active text/node selectionSelection isSelectionEditable(none) booleanWhether the current selection sits inside editable content Selection isPositionEditableposition: numberbooleanWhether positionis inside editable contentInsertion canInsertname: string, position?: number, insertMode?: 'after' | 'before' | 'rename'booleanWhether namecan be inserted atposition(or cursor)Insertion getInsertPositionname: string, position?: number, insertMode?: 'after' | 'before' | 'rename'number | nullValid insertion position for name, ornullif not insertableInsertion getNodeXmlnodeName: string, nodeContent?: stringstring | nullXML template for a node type Wrap/Rename getValidWrapNodeElementNames(none) string[]Element names that may wrap the current selection Wrap/Rename getValidRenameNodeElementNames(none) string[]Element names the current node may be renamed to Tables getTableInfoposition?: number{ rows: number, cols: number, header: number }Dimensions of the current table Tag View isTagViewActive(none) booleanWhether tag-view rendering is active Example: get cursor position and ancestor names
code language-js const textPos = guides.editor.runUtil("getTextPos"); const ancestorNames = guides.editor.runUtil( "getAncestorsNames", typeof textPos === "number" ? textPos : undefined );Example: find all
<xref>elements and read their attributescode language-js const xrefRanges = guides.editor.runUtil("findPositionRanges", "xref") || []; xrefRanges.forEach((range) => { const id = guides.editor.runUtil("getAttributeAtPosition", range.from, "id"); const placeholderText = guides.editor.runUtil( "getAttributeAtPosition", range.from, "placeholdertext" ); });Example: find the root element range and read its attributes
code language-js const rootRange = guides.editor.runUtil("findPositionRange", "concept"); // or "topic" if (rootRange) { const createdDate = guides.editor.runUtil( "getAttributeAtPosition", rootRange.from, "createdDate" ); const author = guides.editor.runUtil( "getAttributeAtPosition", rootRange.from, "author" ); }Example: check selection and validate ancestor context before an operation
code language-js const ancestorsDetails = guides.editor.runUtil('getAncestorsDetails'); const selectedText = guides.editor.runUtil('getSelectedPlainText'); const hasSelection = !!guides.editor.runUtil('hasSelection'); const firstAncestor = ancestorsDetails?.currNode || ancestorsDetails?.ancestors?.[0]?.tagName; if (firstAncestor === 'ph') { tcx.util.showAlert("warning", "Operation is not allowed inside this element type."); return; }Example: get element IDs from ancestor XPaths
code language-js const ancestorItems = guides.editor.runUtil('getAncestorXpaths', true); for (const item of ancestorItems) { const attrs = guides.editor.runUtil('getSerializableAttributes', item.xpath); if (attrs?.id) { /* use element ID */ } }
Decoration API
The Decoration API provides a higher-level alternative to writing full ProseMirror plugins for common visual customizations. Instead of managing Plugin, PluginKey, and DecorationSet manually, you describe what to decorate and how, and the editor’s central decoration manager handles the ProseMirror state internally.
Decorations are identified by a string id so they can be updated or removed independently at any time.
version === '1.0.0').-
guides.editor.addDecoration(id, selector, options): Adds or replaces a decoration rule. All nodes matchingselectorin the current document will be decorated according tooptions. The decoration is re-applied automatically on every document change.Signature:
code language-ts guides.editor.addDecoration( id: string, selector: string, options: DecorationOptions ): booleanDecorationOptionsfields:table 0-row-3 1-row-3 2-row-3 3-row-3 Field Type Description classstringCSS class name(s) added to matching nodes computeAttributes(node, context) => Record<string, string>Function returning dynamic data-*or other HTML attributes per nodefilter(node) => booleanOptional predicate — only nodes where this returns trueare decoratedThe
contextobject passed tocomputeAttributesincludes:index— 0-based position of the node among siblings matching the selector
Example: add a CSS class to all
<section>elementscode language-js guides.editor.addDecoration('highlight-sections', 'section', { class: 'my-section-highlight' });Example: add a computed
data-number-labelattribute to each sectioncode language-js guides.editor.addDecoration('section-numbers', 'section', { computeAttributes: (node, context) => ({ 'data-number-label': String(context.index + 1) }) });Example: decorate only sections that have
importance="high"attributecode language-js guides.editor.addDecoration('important-sections', 'section', { class: 'section-important', filter: (node) => node.attrs?.xmlAttrs?.importance === 'high' });Example: combine class and computed attributes
code language-js guides.editor.addDecoration('numbering-mode', 'conbody', { class: 'legacy-numbering' }); guides.editor.addDecoration('section-numbers', 'section', { computeAttributes: (node, context) => ({ 'data-number-label': String(context.index + 1) }) }); -
guides.editor.removeDecoration(id): Removes a previously added decoration by its ID.Signature:
code language-ts guides.editor.removeDecoration(id: string): booleanExample:
code language-js guides.editor.removeDecoration('section-numbers'); -
guides.editor.batchDecorations(changes): Applies multiple add/remove decoration changes in a single ProseMirror dispatch. Use this when toggling several decorations at once to avoid multiple re-renders.Signature:
code language-ts guides.editor.batchDecorations(changes: DecorationBatchChange[]): boolean type DecorationBatchChange = | { action: 'add', id: string, selector: string, options: DecorationOptions } | { action: 'remove', id: string }Example: switch between two numbering modes atomically
code language-js guides.editor.batchDecorations([ { action: 'remove', id: 'legacy-numbering' }, { action: 'add', id: 'division-numbering', selector: 'conbody', options: { class: 'division-numbering' } } ]);Example: clear one decoration and add two new ones
code language-js guides.editor.batchDecorations([ { action: 'remove', id: 'old-highlights' }, { action: 'add', id: 'section-labels', selector: 'section', options: { computeAttributes: (node, ctx) => ({ 'data-section-index': String(ctx.index) }) } }, { action: 'add', id: 'note-style', selector: 'note', options: { class: 'styled-note' } } ]); -
guides.editor.clearDecorations(): Removes all active decorations managed by the decoration manager.Signature:
code language-ts guides.editor.clearDecorations(): booleanExample:
code language-js guides.editor.clearDecorations(); -
guides.editor.getDecorations(): Returns the IDs of all currently active decorations.Signature:
code language-ts guides.editor.getDecorations(): string[]Example:
code language-js const active = guides.editor.getDecorations(); console.log('Active decorations:', active); // e.g. ['section-numbers', 'numbering-mode', 'note-style']
Decoration API vs registerPlugin
data-* attributes to matching elementsaddDecorationaddDecoration / removeDecoration / batchDecorationsregisterPluginregisterPlugin with css fieldProseMirror plugin registration
-
guides.editor.registerPlugin(factory): Registers a ProseMirror plugin factory to be included in every New Editor instance. Only factory functions are accepted, direct plugin instances are rejected. The factory is called once per editor instance, ensuring isolated plugin state.Signature:
code language-ts guides.editor.registerPlugin(factory: () => PluginConfig): void interface PluginConfig { plugin: ProseMirrorPlugin | ProseMirrorPlugin[] css?: string // Injected into editor's shadow DOM }Example: register plugins after the editor is ready
code language-js guides.ready(() => { guides.editor.registerPlugin(createNumberingPlugin); guides.editor.registerPlugin(createXrefPlugin); });Example: inline factory with CSS
code language-js guides.editor.registerPlugin(() => ({ plugin: new guides.editor.prosemirror.state.Plugin({ key: new guides.editor.prosemirror.state.PluginKey("myPlugin"), props: { decorations(state) { // return DecorationSet... } } }), css: `.my-decoration { background: yellow; }` }));note NOTE CSS passed via cssis injected into the editor’s shadow DOM. Regular page-level stylesheets do not apply inside the editor.
ProseMirror libraries
-
guides.editor.prosemirror: Exposes ProseMirror packages directly for use in plugin development. This avoids the need to bundle ProseMirror separately in extension code.table 0-row-2 1-row-2 2-row-2 3-row-2 4-row-2 5-row-2 6-row-2 7-row-2 8-row-2 9-row-2 10-row-2 Property Package stateprosemirror-statemodelprosemirror-modelviewprosemirror-viewtransformprosemirror-transformcommandsprosemirror-commandskeymapprosemirror-keymaphistoryprosemirror-historytablesprosemirror-tablesdropcursorprosemirror-dropcursormarkdownprosemirror-markdownExample: Create a node decoration plugin
code language-js const myPluginKey = new guides.editor.prosemirror.state.PluginKey("myPlugin"); const createMyPlugin = () => { const { Plugin } = guides.editor.prosemirror.state; const { Decoration, DecorationSet } = guides.editor.prosemirror.view; return { plugin: new Plugin({ key: myPluginKey, state: { init() { return { enabled: true }; }, apply(tr, value) { const meta = tr.getMeta(myPluginKey); return meta ? { ...value, ...meta } : value; } }, props: { decorations(state) { const decorations = []; state.doc.descendants((node, pos) => { if (node.type.name === "section") { decorations.push( Decoration.node(pos, pos + node.nodeSize, { class: "my-section" }) ); } }); return DecorationSet.create(state.doc, decorations); } } }), css: `.my-section { border-left: 3px solid #ccc; padding-left: 8px; }` }; };Example: create a widget decoration plugin (inline display text)
code language-js const xrefPluginKey = new guides.editor.prosemirror.state.PluginKey("xrefDisplay"); const createXrefPlugin = () => { const { Plugin } = guides.editor.prosemirror.state; const { Decoration, DecorationSet } = guides.editor.prosemirror.view; return { plugin: new Plugin({ key: xrefPluginKey, props: { decorations(state) { const decorations = []; state.doc.descendants((node, pos) => { if (node.type.name.includes("xref")) { const display = node.attrs?.xmlAttrs?.placeholdertext; if (display) { decorations.push( Decoration.widget(pos + 1, () => { const el = document.createElement("span"); el.textContent = `[${display}]`; el.setAttribute("contenteditable", "false"); return el; }, { key: `xref-${pos}` }) ); } } }); return DecorationSet.create(state.doc, decorations); } } }), css: `.xref-display-text { color: #0074d9; pointer-events: none; }` }; };
Injecting CSS into the editor
The Guides DITA editor loads its author-mode content styles from a clientlib with category apps.guides.dita_editor.content. That clientlib has an embed declaration that automatically pulls in any clientlib registered under the category:
apps.guides.xml_editor.dita_content_overrides
To inject custom CSS into the editor’s content area, create an AEM clientlib node with this category. No additional wiring is needed, Guides picks it up automatically when the editor page loads.
AEM clientlib node structure (/apps/my-extension/clientlibs/editor-content-overrides/.content.xml)
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"
categories="[apps.guides.xml_editor.dita_content_overrides]"/>
Place your CSS file (css.txt + your .css file) inside this folder. It will be embedded into the editor’s content stylesheet automatically.
Example: override section heading styles in author mode
/* /apps/my-extension/clientlibs/editor-content-overrides/css/content.css */
/* Increase section title font size in author mode */
.section > title {
font-size: 1.4em;
font-weight: bold;
color: #333;
}
/* Highlight note blocks */
.note {
border-left: 4px solid #0074d9;
padding-left: 12px;
background: #f0f7ff;
}
New Editor: css in registerPlugin
The New Editor renders inside a shadow DOM, which isolates it from all page-level styles, including AEM clientlibs. To inject CSS into the New Editor’s author surface, pass a css string as part of the PluginConfig returned by your plugin factory.
The CSS is injected as a <style> tag directly inside the editor’s shadow root each time an editor instance is created.
Example: inject styles alongside a decoration plugin
guides.ready(() => {
guides.editor.registerPlugin(() => ({
plugin: createMyPlugin(), // your ProseMirror plugin
css: `
/* Styles scoped to the New Editor shadow DOM */
.section > .title-node {
font-size: 1.4em;
font-weight: bold;
color: #333;
}
.note-node {
border-left: 4px solid #0074d9;
padding-left: 12px;
background: #f0f7ff;
}
`
}));
});
Example: CSS-only plugin (no decoration logic)
guides.ready(() => {
guides.editor.registerPlugin(() => ({
plugin: new guides.editor.prosemirror.state.Plugin({}), // no-op plugin
css: `
/* Custom author-mode typography */
[data-xml-element="codeblock"] {
font-family: monospace;
background: #f5f5f5;
padding: 8px;
border-radius: 4px;
}
`
}));
});
Context Menu extensions (contextMenuWidget)
Extensions can add items to the editor’s right-click / breadcrumb context menu by declaring a contextMenuWidget field in their extension config. This tells the framework which editor’s menu to target.
Widget IDs
dita_editor_menumarkup_editor_menuBoth widgets are mounted on the page simultaneously. The framework matches each extension to the correct menu based on this field.
Extensions can also target both editors by passing an array:
contextMenuWidget: ["dita_editor_menu", "markup_editor_menu"]
Legacy editor: dita_editor_menu
Use this for extensions that should appear in the legacy CKEditor context menu.
const myContextMenuExtension = {
id: "dita_author_view_menu",
contextMenuWidget: "dita_editor_menu",
view: {
items: [
{
displayName: "My Custom Action",
data: { eventid: "myCustomAction" },
icon: "textSpaceAfter",
target: {
key: "displayName",
value: "Wrap Element", // insert after this existing menu item
viewState: "append",
},
},
],
},
controller: {
myCustomAction() {
console.log("Custom action triggered");
},
},
};
New Editor: markup_editor_menu
Use this for extensions that should appear in the New Editor context menu (breadcrumbs and right-click on elements). The controller receives events via handleExtensionEvent, which routes local handlers to the markup_editor_menu controller and dispatches global events through the app event handler.
const myMarkupContextMenu = {
id: "dita_author_view_menu",
contextMenuWidget: "markup_editor_menu",
view: {
items: [
{
displayName: "Edit Cross Reference",
data: { eventid: "editCrossReference" },
icon: "link",
target: {
key: "displayName",
value: "Cut",
viewState: "prepend",
},
},
{
displayName: "Remove Cross Reference",
data: { eventid: "removeCrossReference" },
icon: "deleteOutline",
target: {
key: "displayName",
value: "Edit Cross Reference",
viewState: "append",
},
},
],
},
controller: {
editCrossReference() {
// Use guides.editor APIs to read context
const ancestorXpaths = guides.editor.runUtil("getAncestorXpaths", true);
// open dialog or take action...
},
removeCrossReference() {
guides.editor.runCommand("unwrapNode");
},
},
};
Utility libraries
-
guides.util.lodash:The lodash utility library, same instance bundled with the editor.code language-js const uniqueIds = guides.util.lodash.uniq(ids); -
guides.util.async: Async utility library for extension use.code language-js guides.util.async.parallel([task1, task2], callback);
Complete API reference
filePathversionappState(key)focus()canInsertXmlElement(tag, insertAfter?)selectCurrentBlockElement()getValidElementNamesForInsertion(args)updateAttributeByXpath(args)runCommand(name, ...args)canRunCommand(name, ...args)runUtil(name, ...args)addDecoration(id, selector, options)removeDecoration(id)batchDecorations(changes)clearDecorations()getDecorations()registerPlugin(factory)prosemirror.stateprosemirror-state packageprosemirror.modelprosemirror-model packageprosemirror.viewprosemirror-view packageprosemirror.transformprosemirror-transform packageprosemirror.commandsprosemirror-commands packageprosemirror.keymapprosemirror-keymap packageprosemirror.historyprosemirror-history packageprosemirror.tablesprosemirror-tables packageprosemirror.dropcursorprosemirror-dropcursor packageprosemirror.collabprosemirror-collab packageprosemirror.markdownprosemirror-markdown package