Maybe this is a general question, not specific to Prosemirror: how can I fire a copy event programmatically? “CTRL+c” works, but I want to fire it with a button in the GUI. In particular I want to make a NodeSelection and put the node on the clipboard.
See navigator.clipboard.writeText
, but note the restrictions.
Thank you, Marijn.
I already saw it, but it seems to work only with simple text.
navigator.clipboard.write is more general, but also much more complex.
I want to copy an entire Prosemirror Node.
I saw the restrictions and the different permissions in different browsers.
Since I need to copy and paste inside the same prosemirror document, perhaps the way around this is implementing a parallel, internal clipboard.
I found this solution:
-
call a
tr.setMeta("copy-as-json", true
) to copy the current selection, ortr.setMeta("copy-as-json", node)
for an arbitrary node -
a
Plugin
intercepts thatsetMeta
and-
copies the stringified JSON of a
Node
or aSlice
in thestate
of thePlugin
, -
executes
navigator.clipboard.writeText(
stringified_node_or_slice_json)
-
-
the
handlePaste
of the samePlugin
checks whether the last copied item on the clipboard is the same as thestate
memorized in thePlugin
-
when the two strings are equal, the Node/Slice is reconstructed with
fromJSON
and the current selection is replaced with it
Here’s the code:
const META_COPY_AS_JSON = 'copy-as-json'
type JsonClipboardItemType = 'node' | 'slice'
class JsonClipboardItem {
constructor(
readonly type: JsonClipboardItemType,
readonly content: string,
) {
}
}
export const JsonPastePlugin = new Plugin({
key: new PluginKey('JsonPaste'),
props: {
handlePaste(view, event, slice) {
const item = this.getState(view.state)
const copiedText = event.clipboardData?.getData('text/plain')
if (item && item.content && item.content === copiedText) {
const { dispatch, state } = view
if (dispatch) {
const json = JSON.parse(item.content)
let tr: Transaction
if (item.type === 'node') {
tr = state.tr.replaceSelectionWith(Node.fromJSON(state.schema, json))
} else {
tr = state.tr.replaceSelection(Slice.fromJSON(state.schema, json))
}
dispatch(tr)
return true
}
}
return false
},
},
state: {
init(): JsonClipboardItem | undefined {
return undefined
},
apply(tr, value, oldState, newState): JsonClipboardItem | undefined {
const metaCopyAsJson = tr.getMeta(META_COPY_AS_JSON)
let type: JsonClipboardItemType | undefined = undefined
let json: string | undefined = undefined
if (metaCopyAsJson === true) {
const { selection } = newState
if (selection instanceof NodeSelection) {
type = 'node'
json = JSON.stringify(selection.node)
} else {
type = 'slice'
json = JSON.stringify(selection.content)
}
} else if (metaCopyAsJson instanceof Node) {
type = 'node'
json = JSON.stringify(metaCopyAsJson)
}
if (type && json) {
navigator.clipboard.writeText(json)
return new JsonClipboardItem(type, json)
}
return value
},
}
})