### Implement Custom MarkView for Links Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use MarkView to provide a custom DOM representation for a mark type. This example creates a 'LinkView' for the 'link' mark, setting its href, title, and target attributes. ```typescript import { EditorView, MarkView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { Mark } from "prosemirror-model" import { schema } from "prosemirror-schema-basic" class LinkView implements MarkView { dom: HTMLAnchorElement contentDOM: HTMLAnchorElement // same element — mark content goes inside constructor(mark: Mark, _view: EditorView, _inline: boolean) { this.dom = document.createElement("a") this.dom.href = mark.attrs.href this.dom.title = mark.attrs.title || mark.attrs.href this.dom.target = "_blank" this.dom.rel = "noopener noreferrer" this.contentDOM = this.dom } destroy() { /* any teardown */ } } const view = new EditorView(document.body, { state: EditorState.create({ schema }), markViews: { link: (mark, view, inline) => new LinkView(mark, view, inline) } }) ``` -------------------------------- ### Handle Editor Events with EditorProps Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Customize editor behavior by returning true from event handlers like handleKeyDown, handleClick, handlePaste, and handleDrop to prevent default processing. This example demonstrates handling Tab key, Ctrl-clicks, and logging paste/drop events. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }), handleKeyDown(view, event) { if (event.key === "Tab") { event.preventDefault() view.dispatch(view.state.tr.insertText(" ")) return true } return false }, handleClick(view, pos, event) { if (event.ctrlKey) { console.log("Ctrl-click at position", pos) return true } return false }, handlePaste(view, event, slice) { console.log("Pasting", slice.content.childCount, "nodes") return false // allow default paste }, handleDrop(view, event, slice, moved) { console.log(`${moved ? "Moved" : "Copied"} content:`, slice) return false }, handleDOMEvents: { focus(view) { view.dom.classList.add("is-focused") return false }, blur(view) { view.dom.classList.remove("is-focused") return false } } }) ``` -------------------------------- ### Implement Custom NodeView for Paragraphs Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use NodeView to provide a fully custom DOM representation for a node type. This example shows a 'FancyParagraph' that adds a custom class and manages its content DOM. ```typescript import { EditorView, NodeView, Decoration, DecorationSource } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { Node } from "prosemirror-model" import { schema } from "prosemirror-schema-basic" class FancyParagraph implements NodeView { dom: HTMLElement contentDOM: HTMLElement constructor( private node: Node, private view: EditorView, private getPos: () => number | undefined ) { this.dom = document.createElement("div") this.dom.className = "fancy-para" const inner = document.createElement("p") this.dom.appendChild(inner) this.contentDOM = inner // ProseMirror renders children here } update(node: Node, _decos: readonly Decoration[], _inner: DecorationSource) { if (node.type !== this.node.type) return false this.node = node this.dom.classList.toggle("has-content", node.content.size > 0) return true } selectNode() { this.dom.classList.add("selected") } deselectNode() { this.dom.classList.remove("selected") } stopEvent(e: Event) { return e.type === "mousedown" } // absorb mousedowns destroy() { /* cleanup */ } } const view = new EditorView(document.body, { state: EditorState.create({ schema }), nodeViews: { paragraph: (node, view, getPos) => new FancyParagraph(node, view, getPos) } }) ``` -------------------------------- ### Serialize Slice for Clipboard Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `serializeForClipboard` to get DOM and text representations of a slice, suitable for clipboard operations. Requires `EditorView`, `EditorState`, and `schema` imports. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" import { TextSelection } from "prosemirror-state" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) // Get the slice for the current selection and put it on the clipboard const slice = view.state.selection.content() const { dom, text } = view.serializeForClipboard(slice) navigator.clipboard.write([ new ClipboardItem({ "text/html": new Blob([dom.innerHTML], { type: "text/html" }), "text/plain": new Blob([text], { type: "text/plain" }) }) ]) ``` -------------------------------- ### Get Viewport Coordinates for Document Position Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `coordsAtPos` to get the bounding box of a document position in viewport coordinates. This is useful for positioning UI elements relative to the cursor. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) function showTooltipAtCursor(view: EditorView) { const { head } = view.state.selection const coords = view.coordsAtPos(head) const tooltip = document.getElementById("tooltip")! tooltip.style.left = coords.left + "px" tooltip.style.top = coords.top + "px" tooltip.hidden = false } // Call whenever selection changes showTooltipAtCursor(view) ``` -------------------------------- ### Get DOM Node for Document Position Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `nodeDOM` to retrieve the outermost DOM node representing the document node immediately after a given position. Returns null if the position is not in front of a node or if the node uses a custom opaque view. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) // Get the bounding rect of the first block node (at position 0) const domNode = view.nodeDOM(0) if (domNode instanceof HTMLElement) { const rect = domNode.getBoundingClientRect() console.log("First node rect:", rect.top, rect.left, rect.width, rect.height) } ``` -------------------------------- ### Create and Mount EditorView Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Instantiates an EditorView and mounts it into the DOM. The `place` argument can be a DOM node, a callback function, or an object with a `mount` property. The `dispatchTransaction` callback is essential for updating the editor state. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" import { exampleSetup } from "prosemirror-example-setup" const state = EditorState.create({ schema, plugins: exampleSetup({ schema }) }) // Mount into a DOM node const view = new EditorView(document.querySelector("#editor")!, { state, dispatchTransaction(tr) { // This is where you integrate with external state management const newState = view.state.apply(tr) view.updateState(newState) } }) // Mount via a function const view2 = new EditorView( (editorDOM) => document.body.prepend(editorDOM), { state } ) // Reuse an existing element const mountEl = document.getElementById("existing-div") as HTMLElement const view3 = new EditorView({ mount: mountEl }, { state }) ``` -------------------------------- ### Import ProseMirror CSS for Rendering Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Import the default prosemirror.css stylesheet to ensure correct rendering of the editor, including whitespace handling, selection display, and node selection outlines. ```typescript // In a bundler (webpack, vite, etc.) import "prosemirror-view/style/prosemirror.css" // Or in HTML: // // The stylesheet defines: // .ProseMirror — root editor element styles // .ProseMirror-selectednode — 2px solid #8cf outline for node selections // .ProseMirror-hideselection — hides native selection (used during drag) // img.ProseMirror-separator — inline separator image reset // You can extend/override without editing the file: const style = document.createElement("style") style.textContent = ` .ProseMirror { min-height: 200px; padding: 8px 12px; outline: none; } .ProseMirror-selectednode { outline-color: #4a90d9; } .search-match { background: rgba(255, 200, 0, 0.4); } ` document.head.appendChild(style) ``` -------------------------------- ### Manage decorations with DecorationSet Source: https://context7.com/prosemirror/prosemirror-view/llms.txt DecorationSet manages decorations efficiently. Use `create` to build a set, `find` to query it, and `map` to update it with document changes using a Mapping. The `onRemove` callback can be used to handle removed decorations. ```typescript import { Decoration, DecorationSet } from "prosemirror-view" import { EditorState, Plugin } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const commentPlugin = new Plugin({ state: { init(_, state) { return DecorationSet.create(state.doc, [ Decoration.inline(1, 4, { class: "comment" }, { id: "c1" }), Decoration.inline(6, 9, { class: "comment" }, { id: "c2" }) ]) }, apply(tr, set) { // Keep decoration positions in sync as the document changes set = set.map(tr.mapping, tr.doc, { onRemove(spec) { console.log("Decoration removed:", spec.id) } }) return set } }, props: { decorations(state) { return commentPlugin.getState(state) } } }) // Query decorations in a range const pluginState = commentPlugin.getState( EditorState.create({ schema, plugins: [commentPlugin] }) )! const inRange = pluginState.find(0, 10, spec => spec.id === "c1") console.log("Found decorations:", inRange.length) // 1 ``` -------------------------------- ### EditorView Constructor Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Creates a new EditorView instance, which renders an EditorState into a DOM element and handles user interactions. The `place` argument can be a DOM node, a callback function, or an object with a `mount` property. The second argument is an object containing editor props, including the `state` and a `dispatchTransaction` callback. ```APIDOC ## EditorView Constructor ### Description Creates the editor view and mounts it into the page. `place` may be a DOM node to append to, a callback `(editor) => void` that inserts the element, or an object `{ mount: HTMLElement }` to reuse an existing element. Pass `null` to keep the view detached. ### Parameters #### `place` - **Type**: DOM Node, `(editor: HTMLElement) => void`, or `{ mount: HTMLElement }` - **Description**: The location or method to mount the editor view. #### `props` - **Type**: Object - **Description**: Configuration object for the editor view. - **`state`** (EditorState) - Required - The initial editor state. - **`dispatchTransaction`** ((tr: Transaction) => void) - Optional - Callback to handle transaction dispatching. ### Request Example ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" import { exampleSetup } from "prosemirror-example-setup" const state = EditorState.create({ schema, plugins: exampleSetup({ schema }) }) // Mount into a DOM node const view = new EditorView(document.querySelector("#editor")!, { state, dispatchTransaction(tr) { // This is where you integrate with external state management const newState = view.state.apply(tr) view.updateState(newState) } }) // Mount via a function const view2 = new EditorView( (editorDOM) => document.body.prepend(editorDOM), { state } ) // Reuse an existing element const mountEl = document.getElementById("existing-div") as HTMLElement const view3 = new EditorView({ mount: mountEl }, { state }) ``` ``` -------------------------------- ### CSS — prosemirror.css Source: https://context7.com/prosemirror/prosemirror-view/llms.txt The package includes a `style/prosemirror.css` stylesheet that must be imported for correct rendering. It defines styles for the root editor element, selected nodes, hidden selections, and separator images. ```APIDOC ## CSS — prosemirror.css The package ships `style/prosemirror.css` which must be imported alongside the JS to ensure correct rendering. It sets up `white-space`, selection hiding, the `.ProseMirror-selectednode` outline, and separator image rules. ```typescript // In a bundler (webpack, vite, etc.) import "prosemirror-view/style/prosemirror.css" // Or in HTML: // // The stylesheet defines: // .ProseMirror — root editor element styles // .ProseMirror-selectednode — 2px solid #8cf outline for node selections // .ProseMirror-hideselection — hides native selection (used during drag) // img.ProseMirror-separator — inline separator image reset // You can extend/override without editing the file: const style = document.createElement("style") style.textContent = ` .ProseMirror { min-height: 200px; padding: 8px 12px; outline: none; } .ProseMirror-selectednode { outline-color: #4a90d9; } .search-match { background: rgba(255, 200, 0, 0.4); } ` document.head.appendChild(style) ``` ``` -------------------------------- ### Create Decoration Widget Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `Decoration.widget` to insert DOM nodes at specific positions without altering the document model. Widgets are automatically repositioned. Requires `EditorView`, `Decoration`, `DecorationSet`, `EditorState`, and `Plugin` imports. ```typescript import { EditorView, Decoration, DecorationSet } from "prosemirror-view" import { EditorState, Plugin } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const cursorPlugin = new Plugin({ props: { decorations(state) { const { head } = state.selection const widget = Decoration.widget( head, (view, getPos) => { const span = document.createElement("span") span.className = "cursor-label" span.textContent = `¶${getPos() ?? "?"}` return span }, { side: 1, // place after cursor key: "cursor-label", // stable key avoids unnecessary redraws destroy(node) { console.log("Widget removed", node) } } ) return DecorationSet.create(state.doc, [widget]) } } }) const view = new EditorView(document.body, { state: EditorState.create({ schema, plugins: [cursorPlugin] }) }) ``` -------------------------------- ### Add and remove decorations using DecorationSet Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Create new DecorationSet instances by adding or removing decorations. The original set remains unchanged. Both `add` and `remove` methods require the current document to rebuild the internal tree structure. ```typescript import { Decoration, DecorationSet } from "prosemirror-view" import { EditorState, Plugin } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" let decoSet = DecorationSet.empty const state = EditorState.create({ schema }) // Add two decorations const d1 = Decoration.inline(1, 3, { class: "highlight" }) const d2 = Decoration.widget(5, document.createElement("span")) decoSet = decoSet.add(state.doc, [d1, d2]) console.log(decoSet.find().length) // 2 // Remove one by identity decoSet = decoSet.remove([d1]) console.log(decoSet.find().length) // 1 ``` -------------------------------- ### MarkView Interface Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Implement the MarkView interface to provide a custom DOM representation for mark types. Mark views offer `dom`, optional `contentDOM`, `ignoreMutation`, and `destroy`. ```APIDOC ## MarkView Interface Implement `MarkView` to give a mark type a custom DOM representation. Unlike `NodeView`, mark views cannot intercept events; they provide only `dom`, optional `contentDOM`, `ignoreMutation`, and `destroy`. ```typescript import { EditorView, MarkView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { Mark } from "prosemirror-model" import { schema } from "prosemirror-schema-basic" class LinkView implements MarkView { dom: HTMLAnchorElement contentDOM: HTMLAnchorElement // same element — mark content goes inside constructor(mark: Mark, _view: EditorView, _inline: boolean) { this.dom = document.createElement("a") this.dom.href = mark.attrs.href this.dom.title = mark.attrs.title || mark.attrs.href this.dom.target = "_blank" this.dom.rel = "noopener noreferrer" this.contentDOM = this.dom } destroy() { /* any teardown */ } } const view = new EditorView(document.body, { state: EditorState.create({ schema }), markViews: { link: (mark, view, inline) => new LinkView(mark, view, inline) } }) ``` ``` -------------------------------- ### Decoration.widget Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Creates a widget decoration that inserts a DOM node at a specific position in the editor's document without altering the document model. These widgets are automatically repositioned as the document content changes. ```APIDOC ## Decoration.widget Inserts an arbitrary DOM node into the rendered document at a given position without modifying the underlying document model. Widgets are repositioned automatically as the document changes. ### Method `Decoration.widget(pos: number, toDOM: (view: EditorView, getPos: () => number | undefined) => HTMLElement, spec?: { side?: number, key?: string, destroy?: (node: HTMLElement) => void }): Decoration` ### Parameters - **pos** (number) - The document position where the widget should be placed. - **toDOM** (function) - A function that returns the DOM element for the widget. It receives the `EditorView` and a `getPos` function. - **spec** (object, optional) - Additional specifications for the widget. - **side** (number, optional) - Determines placement relative to the cursor (e.g., 1 for after, -1 for before). - **key** (string, optional) - A stable key to prevent unnecessary redraws. - **destroy** (function, optional) - A callback function executed when the widget is removed. ### Response - **Decoration** - A widget decoration object. ### Request Example ```typescript import { EditorView, Decoration, DecorationSet } from "prosemirror-view" import { EditorState, Plugin } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const cursorPlugin = new Plugin({ props: { decorations(state) { const { head } = state.selection const widget = Decoration.widget( head, (view, getPos) => { const span = document.createElement("span") span.className = "cursor-label" span.textContent = `¶${getPos() ?? "?"}` return span }, { side: 1, key: "cursor-label", destroy(node) { console.log("Widget removed", node) } } ) return DecorationSet.create(state.doc, [widget]) } } }) const view = new EditorView(document.body, { state: EditorState.create({ schema, plugins: [cursorPlugin] }) }) ``` ``` -------------------------------- ### NodeView Interface Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Implement the NodeView interface to provide a custom DOM representation, event handling, and nested contentDOM for specific node types in ProseMirror. ```APIDOC ## NodeView Interface Implement `NodeView` to give a specific node type a fully custom DOM representation, event handling, and optional nested `contentDOM` for ProseMirror-managed child rendering. ```typescript import { EditorView, NodeView, Decoration, DecorationSource } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { Node } from "prosemirror-model" import { schema } from "prosemirror-schema-basic" class FancyParagraph implements NodeView { dom: HTMLElement contentDOM: HTMLElement constructor( private node: Node, private view: EditorView, private getPos: () => number | undefined ) { this.dom = document.createElement("div") this.dom.className = "fancy-para" const inner = document.createElement("p") this.dom.appendChild(inner) this.contentDOM = inner // ProseMirror renders children here } update(node: Node, _decos: readonly Decoration[], _inner: DecorationSource) { if (node.type !== this.node.type) return false this.node = node this.dom.classList.toggle("has-content", node.content.size > 0) return true } selectNode() { this.dom.classList.add("selected") } deselectNode() { this.dom.classList.remove("selected") } stopEvent(e: Event) { return e.type === "mousedown" } // absorb mousedowns destroy() { /* cleanup */ } } const view = new EditorView(document.body, { state: EditorState.create({ schema }), nodeViews: { paragraph: (node, view, getPos) => new FancyParagraph(node, view, getPos) } }) ``` ``` -------------------------------- ### EditorView.domAtPos / EditorView.posAtDOM Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Provides bidirectional mapping between document positions and the real DOM. `domAtPos` maps a document position to a DOM node and offset, while `posAtDOM` performs the reverse conversion. ```APIDOC ## EditorView.domAtPos / EditorView.posAtDOM ### Description `domAtPos` maps a document position to `{ node, offset }` in the real DOM. `posAtDOM` does the reverse. Both are useful when bridging ProseMirror and raw DOM APIs. ### Methods - `domAtPos(pos: number, bias?: 1 | -1): { node: Node, offset: number }` - `posAtDOM(node: Node, offset: number): number` ### Parameters #### `domAtPos` Parameters - **pos** (number) - The document position to map. - **bias** (number, optional) - Determines whether to prefer the start (-1) or end (1) of a node if the position falls within it. Defaults to 1. #### `posAtDOM` Parameters - **node** (Node) - The DOM node. - **offset** (number) - The offset within the DOM node. ### Request Example ```typescript // Find the DOM node at position 3 const { node, offset } = view.domAtPos(3, 1) // Translate a DOM position back const pos = view.posAtDOM(node, offset) ``` ### Response #### `domAtPos` Success Response - **node** (Node) - The DOM node corresponding to the position. - **offset** (number) - The offset within the DOM node. #### `posAtDOM` Success Response - (number) - The document position. #### Error Handling `posAtDOM` may throw an error if the DOM position is outside the editor. ``` -------------------------------- ### Apply Visual Decorations with the decorations Prop Source: https://context7.com/prosemirror/prosemirror-view/llms.txt The decorations prop accepts a function that returns a DecorationSource to visually annotate the document based on the current state. This function is invoked on every state update. ```typescript import { EditorView, DecorationSet, Decoration } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }), decorations(state) { const decos: Decoration[] = [] // Add a gutter widget at the start of each block node state.doc.forEach((node, offset) => { const gutter = document.createElement("span") gutter.className = "line-number" gutter.textContent = "¶" decos.push(Decoration.widget(offset + 1, gutter, { side: -1 })) }) // Highlight the entire selection range const { from, to } = state.selection if (from !== to) { decos.push(Decoration.inline(from, to, { class: "selection-highlight" })) } return DecorationSet.create(state.doc, decos) } }) ``` -------------------------------- ### Query Editor Properties with someProp Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `someProp` to query a property value across direct props, direct plugins, and state plugins, returning the first truthy result. Useful for custom plugin logic. Requires `Plugin`, `EditorState`, `EditorView`, and `schema` imports. ```typescript import { Plugin, EditorState } from "prosemirror-state" import { EditorView } from "prosemirror-view" import { schema } from "prosemirror-schema-basic" const analyticsPlugin = new Plugin({ props: { handleClick(_view, pos) { console.log("Click at position", pos) return false } } }) const view = new EditorView(document.body, { state: EditorState.create({ schema, plugins: [analyticsPlugin] }), // Check if ANY prop declares a custom editable guard editable: (state) => true }) // Query a prop value across all sources const isEditable = view.someProp("editable", fn => fn(view.state)) console.log("Editor is editable:", isEditable !== false) ``` -------------------------------- ### Map Between DOM and Document Positions Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `domAtPos` to find the DOM node and offset for a document position, and `posAtDOM` to do the reverse. These are essential for integrating with raw DOM APIs. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) // Find the DOM node at position 3 (prefer content after the position) const { node, offset } = view.domAtPos(3, 1) console.log("DOM node:", node.nodeName, "offset:", offset) // Translate a DOM position back (e.g. from a custom event handler) try { const pos = view.posAtDOM(node, offset) console.log("Document position:", pos) } catch (e) { console.warn("DOM position is outside the editor") } ``` -------------------------------- ### EditorView.someProp Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Queries a specific property across all plugin resolvers and direct props of the editor view. It returns the first truthy value found by applying a given function to each non-undefined property value. ```APIDOC ## EditorView.someProp Iterates prop resolvers (direct props → direct plugins → state plugins) and returns the first truthy result of calling `f` on each non-undefined value. Used internally and useful in plugins. ### Method `someProp(propName: string, f: (value: any) => any): any` ### Parameters - **propName** (string) - The name of the property to query. - **f** (function) - A function to apply to each property value. It should return a truthy value to stop iteration and return the result, or a falsy value to continue. ### Response - **any** - The first truthy result returned by the function `f`, or `undefined` if no truthy result is found. ### Request Example ```typescript import { Plugin, EditorState } from "prosemirror-state" import { EditorView } from "prosemirror-view" import { schema } from "prosemirror-schema-basic" const analyticsPlugin = new Plugin({ props: { handleClick(_view, pos) { console.log("Click at position", pos) return false } } }) const view = new EditorView(document.body, { state: EditorState.create({ schema, plugins: [analyticsPlugin] }), editable: (state) => true }) const isEditable = view.someProp("editable", fn => fn(view.state)) console.log("Editor is editable:", isEditable !== false) ``` ``` -------------------------------- ### EditorProps - Event Handlers Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Handle various editor events such as keydown, click, paste, and drop by returning `true` to prevent further processing, or `false`/`undefined` to allow default behavior. ```APIDOC ## EditorProps — event handler props All event-handler props (`handleKeyDown`, `handleClick`, `handlePaste`, `handleDrop`, etc.) follow the same pattern: return `true` to mark the event as handled (preventing further processing), or `false`/`undefined` to pass through. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }), handleKeyDown(view, event) { if (event.key === "Tab") { event.preventDefault() view.dispatch(view.state.tr.insertText(" ")) return true } return false }, handleClick(view, pos, event) { if (event.ctrlKey) { console.log("Ctrl-click at position", pos) return true } return false }, handlePaste(view, event, slice) { console.log("Pasting", slice.content.childCount, "nodes") return false // allow default paste }, handleDrop(view, event, slice, moved) { console.log(`${moved ? "Moved" : "Copied"} content:`, slice) return false }, handleDOMEvents: { focus(view) { view.dom.classList.add("is-focused") return false }, blur(view) { view.dom.classList.remove("is-focused") return false } } }) ``` ``` -------------------------------- ### Programmatically Trigger Paste Operations Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `pasteHTML` and `pasteText` to simulate pasting content into the editor. These methods respect configured paste transformation and handling hooks. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }), handlePaste(view, _event, slice) { console.log("Pasted slice has", slice.content.childCount, "top-level nodes") return false // let default handling proceed } }) // Simulate an HTML paste view.pasteHTML("

Bold text pasted programmatically

") // Simulate a plain-text paste view.pasteText("Plain text\nWith two lines") ``` -------------------------------- ### EditorView.serializeForClipboard Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Serializes a Slice of content from the editor's current selection into a format suitable for clipboard operations. It returns an object containing DOM nodes, plain text, and the slice itself. ```APIDOC ## EditorView.serializeForClipboard Serializes a `Slice` the same way the editor would for a clipboard copy operation, returning `{ dom, text, slice }`. The `dom` element can be inspected or used with the Clipboard API. ### Method `serializeForClipboard(slice: Slice): { dom: HTMLElement, text: string, slice: Slice }` ### Parameters - **slice** (Slice) - The slice of content to serialize. ### Response - **dom** (HTMLElement) - A DOM element representing the serialized content (e.g., for HTML). - **text** (string) - The plain text representation of the serialized content. - **slice** (Slice) - The original slice object. ### Request Example ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) const slice = view.state.selection.content() const { dom, text } = view.serializeForClipboard(slice) navigator.clipboard.write([ new ClipboardItem({ "text/html": new Blob([dom.innerHTML], { type: "text/html" }), "text/plain": new Blob([text], { type: "text/plain" }) }) ]) ``` ``` -------------------------------- ### EditorProps — decorations prop Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Provides a function that returns a `DecorationSource` to visually decorate the document based on the current state. This function is invoked on every state update. ```APIDOC ## EditorProps — decorations prop Provide a function returning a `DecorationSource` to decorate the document in response to the current state. Called on every state update. ```typescript import { EditorView, DecorationSet, Decoration } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }), decorations(state) { const decos: Decoration[] = [] // Add a gutter widget at the start of each block node state.doc.forEach((node, offset) => { const gutter = document.createElement("span") gutter.className = "line-number" gutter.textContent = "¶" decos.push(Decoration.widget(offset + 1, gutter, { side: -1 })) }) // Highlight the entire selection range const { from, to } = state.selection if (from !== to) { decos.push(Decoration.inline(from, to, { class: "selection-highlight" })) } return DecorationSet.create(state.doc, decos) } }) ``` ``` -------------------------------- ### EditorView.setProps / EditorView.update Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Updates the properties of the editor view. `setProps` merges new properties with existing ones, while `update` replaces all properties with a new set. Both methods trigger an immediate DOM update. ```APIDOC ## EditorView.setProps / EditorView.update ### Description `update` replaces **all** props at once; `setProps` merges a partial object into the existing props. Both trigger an immediate DOM update. ### Parameters #### `props` (for `setProps`) - **Type**: Object - **Description**: An object containing properties to merge into the existing props. #### `props` (for `update`) - **Type**: Object - **Description**: An object containing all properties to set for the editor view. ### Request Example ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" import { DecorationSet, Decoration } from "prosemirror-view" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) // Add a decorations prop without rebuilding everything else view.setProps({ decorations(state) { // Highlight positions 1-5 in yellow return DecorationSet.create(state.doc, [ Decoration.inline(1, 5, { style: "background: yellow" }) ]) } }) // Make the editor read-only view.setProps({ editable: () => false }) // Full replacement: provide all required fields view.update({ state: view.state, editable: () => true, attributes: { class: "my-editor", "aria-label": "Rich text editor" } }) ``` ``` -------------------------------- ### Convert Coordinates to Document Position Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use `posAtCoords` to find the document position corresponding to viewport coordinates. It returns the document position and the position of the surrounding node. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }), handleClick(view, _pos, event) { const result = view.posAtCoords({ left: event.clientX, top: event.clientY }) if (result) { console.log("Clicked at doc position:", result.pos) console.log("Inside node at:", result.inside) // -1 means top-level } return false } }) ``` -------------------------------- ### Apply DOM attributes to inline nodes with Decoration.inline Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use Decoration.inline to apply DOM attributes to all inline nodes within a document range. This is useful for highlights, search matches, or annotations. Ensure necessary imports from 'prosemirror-view' and 'prosemirror-state'. ```typescript import { EditorView, Decoration, DecorationSet } from "prosemirror-view" import { EditorState, Plugin } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" // Highlight all occurrences of a search term function buildSearchDecorations(state: EditorState, term: string) { const decos: Decoration[] = [] const regex = new RegExp(term, "gi") state.doc.descendants((node, pos) => { if (!node.isText) return let match while ((match = regex.exec(node.text!)) !== null) { decos.push( Decoration.inline(pos + match.index, pos + match.index + match[0].length, { class: "search-match", style: "background: #ff06" }) ) } }) return DecorationSet.create(state.doc, decos) } const searchPlugin = new Plugin({ props: { decorations(state) { return buildSearchDecorations(state, "prosemirror") } } }) const view = new EditorView(document.body, { state: EditorState.create({ schema, plugins: [searchPlugin] }) }) ``` -------------------------------- ### EditorView.posAtCoords Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Converts viewport coordinates (left, top) into a document position. It returns an object containing the document position (`pos`) and the position of the surrounding node (`inside`). Returns `null` if the coordinates are outside the editor. ```APIDOC ## EditorView.posAtCoords ### Description Converts viewport `(left, top)` coordinates into a document position. Returns `{ pos, inside }` where `inside` is the position of the surrounding node, or `null` if the coordinates are outside the editor. ### Method `posAtCoords(coords: { left: number, top: number }): { pos: number, inside: number } | null` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript const result = view.posAtCoords({ left: event.clientX, top: event.clientY }) if (result) { console.log("Clicked at doc position:", result.pos) console.log("Inside node at:", result.inside) } ``` ### Response #### Success Response - **pos** (number) - The document position. - **inside** (number) - The position of the surrounding node, or -1 if at the top level. #### Response Example ```json { "pos": 10, "inside": 5 } ``` ``` -------------------------------- ### Dispatch Transaction with EditorView Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Dispatches a transaction to the editor. If a `dispatchTransaction` prop is set, it will be called; otherwise, the transaction is applied automatically. This method is pre-bound to the view instance. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) // Insert text at the current selection head const { tr } = view.state tr.insertText("Hello, ProseMirror!") view.dispatch(tr) // Bold the entire first paragraph (positions 1 to 4 in a trivial doc) const boldMark = schema.marks.strong.create() const tr2 = view.state.tr.addMark(1, 4, boldMark) view.dispatch(tr2) ``` -------------------------------- ### Manage Editor Focus Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Check if the editor has focus using `hasFocus` and programmatically focus it with `focus`. Useful for UI feedback and ensuring editor interactivity. Requires `EditorView`, `EditorState`, and `schema` imports. ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) document.getElementById("focus-btn")!.addEventListener("click", () => { if (!view.hasFocus()) { view.focus() console.log("Editor focused") } }) ``` -------------------------------- ### EditorView.coordsAtPos Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Returns a viewport rectangle (`{ left, right, top, bottom }`) for a given document position. This is useful for positioning UI elements like tooltips or menus relative to the cursor. ```APIDOC ## EditorView.coordsAtPos ### Description Returns a `{ left, right, top, bottom }` viewport rectangle for a given document position. Useful for positioning tooltips, menus, or inline widgets relative to the cursor. ### Method `coordsAtPos(pos: number): { left: number, right: number, top: number, bottom: number }` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript const coords = view.coordsAtPos(head) ``` ### Response #### Success Response - **left** (number) - The left coordinate of the rectangle. - **right** (number) - The right coordinate of the rectangle. - **top** (number) - The top coordinate of the rectangle. - **bottom** (number) - The bottom coordinate of the rectangle. #### Response Example ```json { "left": 100, "right": 150, "top": 50, "bottom": 65 } ``` ``` -------------------------------- ### Apply DOM attributes to a specific block node with Decoration.node Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Use Decoration.node to apply DOM attributes to a single block or inline-block node. The decoration's boundaries must precisely align with the node's. The `spec` object can store arbitrary metadata. ```typescript import { EditorView, Decoration, DecorationSet } from "prosemirror-view" import { EditorState, Plugin, NodeSelection } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" // Highlight the currently selected node const selectedNodePlugin = new Plugin({ props: { decorations(state) { const { selection } = state if (!(selection instanceof NodeSelection)) return null return DecorationSet.create(state.doc, [ Decoration.node(selection.from, selection.to, { class: "node-selected", style: "outline: 2px solid royalblue" }, { type: "node-highlight" }) ]) } } }) const view = new EditorView(document.body, { state: EditorState.create({ schema, plugins: [selectedNodePlugin] }) }) ``` -------------------------------- ### EditorView.hasFocus / EditorView.focus Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Manages the focus state of the editor. `hasFocus` checks if the editor element currently has focus, while `focus` programmatically sets the focus to the editor without causing the page to scroll. ```APIDOC ## EditorView.hasFocus / EditorView.focus `hasFocus` returns whether the editor element is the active focused element. `focus` programmatically focuses the editor without scrolling the page. ### Methods - **hasFocus(): boolean** - Returns `true` if the editor has focus, `false` otherwise. - **focus(): void** - Programmatically sets focus to the editor. ### Request Example ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) document.getElementById("focus-btn")!.addEventListener("click", () => { if (!view.hasFocus()) { view.focus() console.log("Editor focused") } }) ``` ``` -------------------------------- ### EditorView.dispatch Source: https://context7.com/prosemirror/prosemirror-view/llms.txt Dispatches a ProseMirror transaction. If a `dispatchTransaction` callback is provided in the editor's props, it will be called with the transaction. Otherwise, the transaction is applied to the current state automatically. This method is pre-bound to the view instance. ```APIDOC ## EditorView.dispatch ### Description Dispatches a transaction. If `dispatchTransaction` is provided in props it is called; otherwise the transaction is applied to the current state automatically. The method is pre-bound to the view instance. ### Parameters #### `tr` - **Type**: Transaction - **Description**: The transaction to dispatch. ### Request Example ```typescript import { EditorView } from "prosemirror-view" import { EditorState } from "prosemirror-state" import { schema } from "prosemirror-schema-basic" const view = new EditorView(document.body, { state: EditorState.create({ schema }) }) // Insert text at the current selection head const { tr } = view.state tr.insertText("Hello, ProseMirror!") view.dispatch(tr) // Bold the entire first paragraph (positions 1 to 4 in a trivial doc) const boldMark = schema.marks.strong.create() const tr2 = view.state.tr.addMark(1, 4, boldMark) view.dispatch(tr2) ``` ```