@tiptap/core
Version:
headless rich text editor
1,566 lines (1,483 loc) • 215 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
CommandManager: () => CommandManager,
Editor: () => Editor,
Extendable: () => Extendable,
Extension: () => Extension,
Fragment: () => Fragment6,
InputRule: () => InputRule,
Mark: () => Mark,
MarkView: () => MarkView,
Node: () => Node3,
NodePos: () => NodePos,
NodeView: () => NodeView,
PasteRule: () => PasteRule,
ResizableNodeview: () => ResizableNodeview,
Tracker: () => Tracker,
callOrReturn: () => callOrReturn,
canInsertNode: () => canInsertNode,
combineTransactionSteps: () => combineTransactionSteps,
commands: () => commands_exports,
createAtomBlockMarkdownSpec: () => createAtomBlockMarkdownSpec,
createBlockMarkdownSpec: () => createBlockMarkdownSpec,
createChainableState: () => createChainableState,
createDocument: () => createDocument,
createElement: () => h,
createInlineMarkdownSpec: () => createInlineMarkdownSpec,
createNodeFromContent: () => createNodeFromContent,
createStyleTag: () => createStyleTag,
defaultBlockAt: () => defaultBlockAt,
deleteProps: () => deleteProps,
elementFromString: () => elementFromString,
escapeForRegEx: () => escapeForRegEx,
extensions: () => extensions_exports,
findChildren: () => findChildren,
findChildrenInRange: () => findChildrenInRange,
findDuplicates: () => findDuplicates,
findParentNode: () => findParentNode,
findParentNodeClosestToPos: () => findParentNodeClosestToPos,
flattenExtensions: () => flattenExtensions,
fromString: () => fromString,
generateHTML: () => generateHTML,
generateJSON: () => generateJSON,
generateText: () => generateText,
getAttributes: () => getAttributes,
getAttributesFromExtensions: () => getAttributesFromExtensions,
getChangedRanges: () => getChangedRanges,
getDebugJSON: () => getDebugJSON,
getExtensionField: () => getExtensionField,
getHTMLFromFragment: () => getHTMLFromFragment,
getMarkAttributes: () => getMarkAttributes,
getMarkRange: () => getMarkRange,
getMarkType: () => getMarkType,
getMarksBetween: () => getMarksBetween,
getNodeAtPosition: () => getNodeAtPosition,
getNodeAttributes: () => getNodeAttributes,
getNodeType: () => getNodeType,
getRenderedAttributes: () => getRenderedAttributes,
getSchema: () => getSchema,
getSchemaByResolvedExtensions: () => getSchemaByResolvedExtensions,
getSchemaTypeByName: () => getSchemaTypeByName,
getSchemaTypeNameByName: () => getSchemaTypeNameByName,
getSplittedAttributes: () => getSplittedAttributes,
getText: () => getText,
getTextBetween: () => getTextBetween,
getTextContentFromNodes: () => getTextContentFromNodes,
getTextSerializersFromSchema: () => getTextSerializersFromSchema,
h: () => h,
injectExtensionAttributesToParseRule: () => injectExtensionAttributesToParseRule,
inputRulesPlugin: () => inputRulesPlugin,
isActive: () => isActive,
isAndroid: () => isAndroid,
isAtEndOfNode: () => isAtEndOfNode,
isAtStartOfNode: () => isAtStartOfNode,
isEmptyObject: () => isEmptyObject,
isExtensionRulesEnabled: () => isExtensionRulesEnabled,
isFunction: () => isFunction,
isList: () => isList,
isMacOS: () => isMacOS,
isMarkActive: () => isMarkActive,
isNodeActive: () => isNodeActive,
isNodeEmpty: () => isNodeEmpty,
isNodeSelection: () => isNodeSelection,
isNumber: () => isNumber,
isPlainObject: () => isPlainObject,
isRegExp: () => isRegExp,
isString: () => isString,
isTextSelection: () => isTextSelection,
isiOS: () => isiOS,
markInputRule: () => markInputRule,
markPasteRule: () => markPasteRule,
markdown: () => markdown_exports,
mergeAttributes: () => mergeAttributes,
mergeDeep: () => mergeDeep,
minMax: () => minMax,
nodeInputRule: () => nodeInputRule,
nodePasteRule: () => nodePasteRule,
objectIncludes: () => objectIncludes,
parseAttributes: () => parseAttributes,
parseIndentedBlocks: () => parseIndentedBlocks,
pasteRulesPlugin: () => pasteRulesPlugin,
posToDOMRect: () => posToDOMRect,
removeDuplicates: () => removeDuplicates,
renderNestedMarkdownContent: () => renderNestedMarkdownContent,
resolveExtensions: () => resolveExtensions,
resolveFocusPosition: () => resolveFocusPosition,
rewriteUnknownContent: () => rewriteUnknownContent,
selectionToInsertionEnd: () => selectionToInsertionEnd,
serializeAttributes: () => serializeAttributes,
sortExtensions: () => sortExtensions,
splitExtensions: () => splitExtensions,
textInputRule: () => textInputRule,
textPasteRule: () => textPasteRule,
textblockTypeInputRule: () => textblockTypeInputRule,
updateMarkViewAttributes: () => updateMarkViewAttributes,
wrappingInputRule: () => wrappingInputRule
});
module.exports = __toCommonJS(index_exports);
// src/helpers/createChainableState.ts
function createChainableState(config) {
const { state, transaction } = config;
let { selection } = transaction;
let { doc } = transaction;
let { storedMarks } = transaction;
return {
...state,
apply: state.apply.bind(state),
applyTransaction: state.applyTransaction.bind(state),
plugins: state.plugins,
schema: state.schema,
reconfigure: state.reconfigure.bind(state),
toJSON: state.toJSON.bind(state),
get storedMarks() {
return storedMarks;
},
get selection() {
return selection;
},
get doc() {
return doc;
},
get tr() {
selection = transaction.selection;
doc = transaction.doc;
storedMarks = transaction.storedMarks;
return transaction;
}
};
}
// src/CommandManager.ts
var CommandManager = class {
constructor(props) {
this.editor = props.editor;
this.rawCommands = this.editor.extensionManager.commands;
this.customState = props.state;
}
get hasCustomState() {
return !!this.customState;
}
get state() {
return this.customState || this.editor.state;
}
get commands() {
const { rawCommands, editor, state } = this;
const { view } = editor;
const { tr } = state;
const props = this.buildProps(tr);
return Object.fromEntries(
Object.entries(rawCommands).map(([name, command2]) => {
const method = (...args) => {
const callback = command2(...args)(props);
if (!tr.getMeta("preventDispatch") && !this.hasCustomState) {
view.dispatch(tr);
}
return callback;
};
return [name, method];
})
);
}
get chain() {
return () => this.createChain();
}
get can() {
return () => this.createCan();
}
createChain(startTr, shouldDispatch = true) {
const { rawCommands, editor, state } = this;
const { view } = editor;
const callbacks = [];
const hasStartTransaction = !!startTr;
const tr = startTr || state.tr;
const run3 = () => {
if (!hasStartTransaction && shouldDispatch && !tr.getMeta("preventDispatch") && !this.hasCustomState) {
view.dispatch(tr);
}
return callbacks.every((callback) => callback === true);
};
const chain = {
...Object.fromEntries(
Object.entries(rawCommands).map(([name, command2]) => {
const chainedCommand = (...args) => {
const props = this.buildProps(tr, shouldDispatch);
const callback = command2(...args)(props);
callbacks.push(callback);
return chain;
};
return [name, chainedCommand];
})
),
run: run3
};
return chain;
}
createCan(startTr) {
const { rawCommands, state } = this;
const dispatch = false;
const tr = startTr || state.tr;
const props = this.buildProps(tr, dispatch);
const formattedCommands = Object.fromEntries(
Object.entries(rawCommands).map(([name, command2]) => {
return [name, (...args) => command2(...args)({ ...props, dispatch: void 0 })];
})
);
return {
...formattedCommands,
chain: () => this.createChain(tr, dispatch)
};
}
buildProps(tr, shouldDispatch = true) {
const { rawCommands, editor, state } = this;
const { view } = editor;
const props = {
tr,
editor,
view,
state: createChainableState({
state,
transaction: tr
}),
dispatch: shouldDispatch ? () => void 0 : void 0,
chain: () => this.createChain(tr, shouldDispatch),
can: () => this.createCan(tr),
get commands() {
return Object.fromEntries(
Object.entries(rawCommands).map(([name, command2]) => {
return [name, (...args) => command2(...args)(props)];
})
);
}
};
return props;
}
};
// src/commands/index.ts
var commands_exports = {};
__export(commands_exports, {
blur: () => blur,
clearContent: () => clearContent,
clearNodes: () => clearNodes,
command: () => command,
createParagraphNear: () => createParagraphNear,
cut: () => cut,
deleteCurrentNode: () => deleteCurrentNode,
deleteNode: () => deleteNode,
deleteRange: () => deleteRange,
deleteSelection: () => deleteSelection,
enter: () => enter,
exitCode: () => exitCode,
extendMarkRange: () => extendMarkRange,
first: () => first,
focus: () => focus,
forEach: () => forEach,
insertContent: () => insertContent,
insertContentAt: () => insertContentAt,
joinBackward: () => joinBackward,
joinDown: () => joinDown,
joinForward: () => joinForward,
joinItemBackward: () => joinItemBackward,
joinItemForward: () => joinItemForward,
joinTextblockBackward: () => joinTextblockBackward,
joinTextblockForward: () => joinTextblockForward,
joinUp: () => joinUp,
keyboardShortcut: () => keyboardShortcut,
lift: () => lift,
liftEmptyBlock: () => liftEmptyBlock,
liftListItem: () => liftListItem,
newlineInCode: () => newlineInCode,
resetAttributes: () => resetAttributes,
scrollIntoView: () => scrollIntoView,
selectAll: () => selectAll,
selectNodeBackward: () => selectNodeBackward,
selectNodeForward: () => selectNodeForward,
selectParentNode: () => selectParentNode,
selectTextblockEnd: () => selectTextblockEnd,
selectTextblockStart: () => selectTextblockStart,
setContent: () => setContent,
setMark: () => setMark,
setMeta: () => setMeta,
setNode: () => setNode,
setNodeSelection: () => setNodeSelection,
setTextSelection: () => setTextSelection,
sinkListItem: () => sinkListItem,
splitBlock: () => splitBlock,
splitListItem: () => splitListItem,
toggleList: () => toggleList,
toggleMark: () => toggleMark,
toggleNode: () => toggleNode,
toggleWrap: () => toggleWrap,
undoInputRule: () => undoInputRule,
unsetAllMarks: () => unsetAllMarks,
unsetMark: () => unsetMark,
updateAttributes: () => updateAttributes,
wrapIn: () => wrapIn,
wrapInList: () => wrapInList
});
// src/commands/blur.ts
var blur = () => ({ editor, view }) => {
requestAnimationFrame(() => {
var _a;
if (!editor.isDestroyed) {
;
view.dom.blur();
(_a = window == null ? void 0 : window.getSelection()) == null ? void 0 : _a.removeAllRanges();
}
});
return true;
};
// src/commands/clearContent.ts
var clearContent = (emitUpdate = true) => ({ commands }) => {
return commands.setContent("", { emitUpdate });
};
// src/commands/clearNodes.ts
var import_transform = require("@tiptap/pm/transform");
var clearNodes = () => ({ state, tr, dispatch }) => {
const { selection } = tr;
const { ranges } = selection;
if (!dispatch) {
return true;
}
ranges.forEach(({ $from, $to }) => {
state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
if (node.type.isText) {
return;
}
const { doc, mapping } = tr;
const $mappedFrom = doc.resolve(mapping.map(pos));
const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
const nodeRange = $mappedFrom.blockRange($mappedTo);
if (!nodeRange) {
return;
}
const targetLiftDepth = (0, import_transform.liftTarget)(nodeRange);
if (node.type.isTextblock) {
const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
tr.setNodeMarkup(nodeRange.start, defaultType);
}
if (targetLiftDepth || targetLiftDepth === 0) {
tr.lift(nodeRange, targetLiftDepth);
}
});
});
return true;
};
// src/commands/command.ts
var command = (fn) => (props) => {
return fn(props);
};
// src/commands/createParagraphNear.ts
var import_commands = require("@tiptap/pm/commands");
var createParagraphNear = () => ({ state, dispatch }) => {
return (0, import_commands.createParagraphNear)(state, dispatch);
};
// src/commands/cut.ts
var import_state = require("@tiptap/pm/state");
var cut = (originRange, targetPos) => ({ editor, tr }) => {
const { state } = editor;
const contentSlice = state.doc.slice(originRange.from, originRange.to);
tr.deleteRange(originRange.from, originRange.to);
const newPos = tr.mapping.map(targetPos);
tr.insert(newPos, contentSlice.content);
tr.setSelection(new import_state.TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))));
return true;
};
// src/commands/deleteCurrentNode.ts
var deleteCurrentNode = () => ({ tr, dispatch }) => {
const { selection } = tr;
const currentNode = selection.$anchor.node();
if (currentNode.content.size > 0) {
return false;
}
const $pos = tr.selection.$anchor;
for (let depth = $pos.depth; depth > 0; depth -= 1) {
const node = $pos.node(depth);
if (node.type === currentNode.type) {
if (dispatch) {
const from = $pos.before(depth);
const to = $pos.after(depth);
tr.delete(from, to).scrollIntoView();
}
return true;
}
}
return false;
};
// src/helpers/getNodeType.ts
function getNodeType(nameOrType, schema) {
if (typeof nameOrType === "string") {
if (!schema.nodes[nameOrType]) {
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
}
return schema.nodes[nameOrType];
}
return nameOrType;
}
// src/commands/deleteNode.ts
var deleteNode = (typeOrName) => ({ tr, state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema);
const $pos = tr.selection.$anchor;
for (let depth = $pos.depth; depth > 0; depth -= 1) {
const node = $pos.node(depth);
if (node.type === type) {
if (dispatch) {
const from = $pos.before(depth);
const to = $pos.after(depth);
tr.delete(from, to).scrollIntoView();
}
return true;
}
}
return false;
};
// src/commands/deleteRange.ts
var deleteRange = (range) => ({ tr, dispatch }) => {
const { from, to } = range;
if (dispatch) {
tr.delete(from, to);
}
return true;
};
// src/commands/deleteSelection.ts
var import_commands2 = require("@tiptap/pm/commands");
var deleteSelection = () => ({ state, dispatch }) => {
return (0, import_commands2.deleteSelection)(state, dispatch);
};
// src/commands/enter.ts
var enter = () => ({ commands }) => {
return commands.keyboardShortcut("Enter");
};
// src/commands/exitCode.ts
var import_commands3 = require("@tiptap/pm/commands");
var exitCode = () => ({ state, dispatch }) => {
return (0, import_commands3.exitCode)(state, dispatch);
};
// src/commands/extendMarkRange.ts
var import_state2 = require("@tiptap/pm/state");
// src/utilities/isRegExp.ts
function isRegExp(value) {
return Object.prototype.toString.call(value) === "[object RegExp]";
}
// src/utilities/objectIncludes.ts
function objectIncludes(object1, object2, options = { strict: true }) {
const keys = Object.keys(object2);
if (!keys.length) {
return true;
}
return keys.every((key) => {
if (options.strict) {
return object2[key] === object1[key];
}
if (isRegExp(object2[key])) {
return object2[key].test(object1[key]);
}
return object2[key] === object1[key];
});
}
// src/helpers/getMarkRange.ts
function findMarkInSet(marks, type, attributes = {}) {
return marks.find((item) => {
return item.type === type && objectIncludes(
// Only check equality for the attributes that are provided
Object.fromEntries(Object.keys(attributes).map((k) => [k, item.attrs[k]])),
attributes
);
});
}
function isMarkInSet(marks, type, attributes = {}) {
return !!findMarkInSet(marks, type, attributes);
}
function getMarkRange($pos, type, attributes) {
var _a;
if (!$pos || !type) {
return;
}
let start = $pos.parent.childAfter($pos.parentOffset);
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
start = $pos.parent.childBefore($pos.parentOffset);
}
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
return;
}
attributes = attributes || ((_a = start.node.marks[0]) == null ? void 0 : _a.attrs);
const mark = findMarkInSet([...start.node.marks], type, attributes);
if (!mark) {
return;
}
let startIndex = start.index;
let startPos = $pos.start() + start.offset;
let endIndex = startIndex + 1;
let endPos = startPos + start.node.nodeSize;
while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
startIndex -= 1;
startPos -= $pos.parent.child(startIndex).nodeSize;
}
while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
endPos += $pos.parent.child(endIndex).nodeSize;
endIndex += 1;
}
return {
from: startPos,
to: endPos
};
}
// src/helpers/getMarkType.ts
function getMarkType(nameOrType, schema) {
if (typeof nameOrType === "string") {
if (!schema.marks[nameOrType]) {
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
}
return schema.marks[nameOrType];
}
return nameOrType;
}
// src/commands/extendMarkRange.ts
var extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
const type = getMarkType(typeOrName, state.schema);
const { doc, selection } = tr;
const { $from, from, to } = selection;
if (dispatch) {
const range = getMarkRange($from, type, attributes);
if (range && range.from <= from && range.to >= to) {
const newSelection = import_state2.TextSelection.create(doc, range.from, range.to);
tr.setSelection(newSelection);
}
}
return true;
};
// src/commands/first.ts
var first = (commands) => (props) => {
const items = typeof commands === "function" ? commands(props) : commands;
for (let i = 0; i < items.length; i += 1) {
if (items[i](props)) {
return true;
}
}
return false;
};
// src/helpers/isTextSelection.ts
var import_state3 = require("@tiptap/pm/state");
function isTextSelection(value) {
return value instanceof import_state3.TextSelection;
}
// src/helpers/resolveFocusPosition.ts
var import_state4 = require("@tiptap/pm/state");
// src/utilities/minMax.ts
function minMax(value = 0, min = 0, max = 0) {
return Math.min(Math.max(value, min), max);
}
// src/helpers/resolveFocusPosition.ts
function resolveFocusPosition(doc, position = null) {
if (!position) {
return null;
}
const selectionAtStart = import_state4.Selection.atStart(doc);
const selectionAtEnd = import_state4.Selection.atEnd(doc);
if (position === "start" || position === true) {
return selectionAtStart;
}
if (position === "end") {
return selectionAtEnd;
}
const minPos = selectionAtStart.from;
const maxPos = selectionAtEnd.to;
if (position === "all") {
return import_state4.TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
}
return import_state4.TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
}
// src/utilities/isAndroid.ts
function isAndroid() {
return navigator.platform === "Android" || /android/i.test(navigator.userAgent);
}
// src/utilities/isiOS.ts
function isiOS() {
return ["iPad Simulator", "iPhone Simulator", "iPod Simulator", "iPad", "iPhone", "iPod"].includes(navigator.platform) || // iPad on iOS 13 detection
navigator.userAgent.includes("Mac") && "ontouchend" in document;
}
// src/commands/focus.ts
var focus = (position = null, options = {}) => ({ editor, view, tr, dispatch }) => {
options = {
scrollIntoView: true,
...options
};
const delayedFocus = () => {
if (isiOS() || isAndroid()) {
;
view.dom.focus();
}
requestAnimationFrame(() => {
if (!editor.isDestroyed) {
view.focus();
if (options == null ? void 0 : options.scrollIntoView) {
editor.commands.scrollIntoView();
}
}
});
};
if (view.hasFocus() && position === null || position === false) {
return true;
}
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
delayedFocus();
return true;
}
const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
const isSameSelection = editor.state.selection.eq(selection);
if (dispatch) {
if (!isSameSelection) {
tr.setSelection(selection);
}
if (isSameSelection && tr.storedMarks) {
tr.setStoredMarks(tr.storedMarks);
}
delayedFocus();
}
return true;
};
// src/commands/forEach.ts
var forEach = (items, fn) => (props) => {
return items.every((item, index) => fn(item, { ...props, index }));
};
// src/commands/insertContent.ts
var insertContent = (value, options) => ({ tr, commands }) => {
return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
};
// src/commands/insertContentAt.ts
var import_model2 = require("@tiptap/pm/model");
// src/helpers/createNodeFromContent.ts
var import_model = require("@tiptap/pm/model");
// src/utilities/elementFromString.ts
var removeWhitespaces = (node) => {
const children = node.childNodes;
for (let i = children.length - 1; i >= 0; i -= 1) {
const child = children[i];
if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
node.removeChild(child);
} else if (child.nodeType === 1) {
removeWhitespaces(child);
}
}
return node;
};
function elementFromString(value) {
if (typeof window === "undefined") {
throw new Error("[tiptap error]: there is no window object available, so this function cannot be used");
}
const wrappedValue = `<body>${value}</body>`;
const html = new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
return removeWhitespaces(html);
}
// src/helpers/createNodeFromContent.ts
function createNodeFromContent(content, schema, options) {
if (content instanceof import_model.Node || content instanceof import_model.Fragment) {
return content;
}
options = {
slice: true,
parseOptions: {},
...options
};
const isJSONContent = typeof content === "object" && content !== null;
const isTextContent = typeof content === "string";
if (isJSONContent) {
try {
const isArrayContent = Array.isArray(content) && content.length > 0;
if (isArrayContent) {
return import_model.Fragment.fromArray(content.map((item) => schema.nodeFromJSON(item)));
}
const node = schema.nodeFromJSON(content);
if (options.errorOnInvalidContent) {
node.check();
}
return node;
} catch (error) {
if (options.errorOnInvalidContent) {
throw new Error("[tiptap error]: Invalid JSON content", { cause: error });
}
console.warn("[tiptap warn]: Invalid content.", "Passed value:", content, "Error:", error);
return createNodeFromContent("", schema, options);
}
}
if (isTextContent) {
if (options.errorOnInvalidContent) {
let hasInvalidContent = false;
let invalidContent = "";
const contentCheckSchema = new import_model.Schema({
topNode: schema.spec.topNode,
marks: schema.spec.marks,
// Prosemirror's schemas are executed such that: the last to execute, matches last
// This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
nodes: schema.spec.nodes.append({
__tiptap__private__unknown__catch__all__node: {
content: "inline*",
group: "block",
parseDOM: [
{
tag: "*",
getAttrs: (e) => {
hasInvalidContent = true;
invalidContent = typeof e === "string" ? e : e.outerHTML;
return null;
}
}
]
}
})
});
if (options.slice) {
import_model.DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
} else {
import_model.DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
}
if (options.errorOnInvalidContent && hasInvalidContent) {
throw new Error("[tiptap error]: Invalid HTML content", {
cause: new Error(`Invalid element found: ${invalidContent}`)
});
}
}
const parser = import_model.DOMParser.fromSchema(schema);
if (options.slice) {
return parser.parseSlice(elementFromString(content), options.parseOptions).content;
}
return parser.parse(elementFromString(content), options.parseOptions);
}
return createNodeFromContent("", schema, options);
}
// src/helpers/selectionToInsertionEnd.ts
var import_state5 = require("@tiptap/pm/state");
var import_transform2 = require("@tiptap/pm/transform");
function selectionToInsertionEnd(tr, startLen, bias) {
const last = tr.steps.length - 1;
if (last < startLen) {
return;
}
const step = tr.steps[last];
if (!(step instanceof import_transform2.ReplaceStep || step instanceof import_transform2.ReplaceAroundStep)) {
return;
}
const map = tr.mapping.maps[last];
let end = 0;
map.forEach((_from, _to, _newFrom, newTo) => {
if (end === 0) {
end = newTo;
}
});
tr.setSelection(import_state5.Selection.near(tr.doc.resolve(end), bias));
}
// src/commands/insertContentAt.ts
var isFragment = (nodeOrFragment) => {
return !("type" in nodeOrFragment);
};
var insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
var _a;
if (dispatch) {
options = {
parseOptions: editor.options.parseOptions,
updateSelection: true,
applyInputRules: false,
applyPasteRules: false,
...options
};
let content;
const emitContentError = (error) => {
editor.emit("contentError", {
editor,
error,
disableCollaboration: () => {
if ("collaboration" in editor.storage && typeof editor.storage.collaboration === "object" && editor.storage.collaboration) {
;
editor.storage.collaboration.isDisabled = true;
}
}
});
};
const parseOptions = {
preserveWhitespace: "full",
...options.parseOptions
};
if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {
try {
createNodeFromContent(value, editor.schema, {
parseOptions,
errorOnInvalidContent: true
});
} catch (e) {
emitContentError(e);
}
}
try {
content = createNodeFromContent(value, editor.schema, {
parseOptions,
errorOnInvalidContent: (_a = options.errorOnInvalidContent) != null ? _a : editor.options.enableContentCheck
});
} catch (e) {
emitContentError(e);
return false;
}
let { from, to } = typeof position === "number" ? { from: position, to: position } : { from: position.from, to: position.to };
let isOnlyTextContent = true;
let isOnlyBlockContent = true;
const nodes = isFragment(content) ? content : [content];
nodes.forEach((node) => {
node.check();
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
});
if (from === to && isOnlyBlockContent) {
const { parent } = tr.doc.resolve(from);
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
if (isEmptyTextBlock) {
from -= 1;
to += 1;
}
}
let newContent;
if (isOnlyTextContent) {
if (Array.isArray(value)) {
newContent = value.map((v) => v.text || "").join("");
} else if (value instanceof import_model2.Fragment) {
let text = "";
value.forEach((node) => {
if (node.text) {
text += node.text;
}
});
newContent = text;
} else if (typeof value === "object" && !!value && !!value.text) {
newContent = value.text;
} else {
newContent = value;
}
tr.insertText(newContent, from, to);
} else {
newContent = content;
const $from = tr.doc.resolve(from);
const $fromNode = $from.node();
const fromSelectionAtStart = $from.parentOffset === 0;
const isTextSelection2 = $fromNode.isText || $fromNode.isTextblock;
const hasContent = $fromNode.content.size > 0;
if (fromSelectionAtStart && isTextSelection2 && hasContent) {
from = Math.max(0, from - 1);
}
tr.replaceWith(from, to, newContent);
}
if (options.updateSelection) {
selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
}
if (options.applyInputRules) {
tr.setMeta("applyInputRules", { from, text: newContent });
}
if (options.applyPasteRules) {
tr.setMeta("applyPasteRules", { from, text: newContent });
}
}
return true;
};
// src/commands/join.ts
var import_commands4 = require("@tiptap/pm/commands");
var joinUp = () => ({ state, dispatch }) => {
return (0, import_commands4.joinUp)(state, dispatch);
};
var joinDown = () => ({ state, dispatch }) => {
return (0, import_commands4.joinDown)(state, dispatch);
};
var joinBackward = () => ({ state, dispatch }) => {
return (0, import_commands4.joinBackward)(state, dispatch);
};
var joinForward = () => ({ state, dispatch }) => {
return (0, import_commands4.joinForward)(state, dispatch);
};
// src/commands/joinItemBackward.ts
var import_transform3 = require("@tiptap/pm/transform");
var joinItemBackward = () => ({ state, dispatch, tr }) => {
try {
const point = (0, import_transform3.joinPoint)(state.doc, state.selection.$from.pos, -1);
if (point === null || point === void 0) {
return false;
}
tr.join(point, 2);
if (dispatch) {
dispatch(tr);
}
return true;
} catch {
return false;
}
};
// src/commands/joinItemForward.ts
var import_transform4 = require("@tiptap/pm/transform");
var joinItemForward = () => ({ state, dispatch, tr }) => {
try {
const point = (0, import_transform4.joinPoint)(state.doc, state.selection.$from.pos, 1);
if (point === null || point === void 0) {
return false;
}
tr.join(point, 2);
if (dispatch) {
dispatch(tr);
}
return true;
} catch {
return false;
}
};
// src/commands/joinTextblockBackward.ts
var import_commands5 = require("@tiptap/pm/commands");
var joinTextblockBackward = () => ({ state, dispatch }) => {
return (0, import_commands5.joinTextblockBackward)(state, dispatch);
};
// src/commands/joinTextblockForward.ts
var import_commands6 = require("@tiptap/pm/commands");
var joinTextblockForward = () => ({ state, dispatch }) => {
return (0, import_commands6.joinTextblockForward)(state, dispatch);
};
// src/utilities/isMacOS.ts
function isMacOS() {
return typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
}
// src/commands/keyboardShortcut.ts
function normalizeKeyName(name) {
const parts = name.split(/-(?!$)/);
let result = parts[parts.length - 1];
if (result === "Space") {
result = " ";
}
let alt;
let ctrl;
let shift;
let meta;
for (let i = 0; i < parts.length - 1; i += 1) {
const mod = parts[i];
if (/^(cmd|meta|m)$/i.test(mod)) {
meta = true;
} else if (/^a(lt)?$/i.test(mod)) {
alt = true;
} else if (/^(c|ctrl|control)$/i.test(mod)) {
ctrl = true;
} else if (/^s(hift)?$/i.test(mod)) {
shift = true;
} else if (/^mod$/i.test(mod)) {
if (isiOS() || isMacOS()) {
meta = true;
} else {
ctrl = true;
}
} else {
throw new Error(`Unrecognized modifier name: ${mod}`);
}
}
if (alt) {
result = `Alt-${result}`;
}
if (ctrl) {
result = `Ctrl-${result}`;
}
if (meta) {
result = `Meta-${result}`;
}
if (shift) {
result = `Shift-${result}`;
}
return result;
}
var keyboardShortcut = (name) => ({ editor, view, tr, dispatch }) => {
const keys = normalizeKeyName(name).split(/-(?!$)/);
const key = keys.find((item) => !["Alt", "Ctrl", "Meta", "Shift"].includes(item));
const event = new KeyboardEvent("keydown", {
key: key === "Space" ? " " : key,
altKey: keys.includes("Alt"),
ctrlKey: keys.includes("Ctrl"),
metaKey: keys.includes("Meta"),
shiftKey: keys.includes("Shift"),
bubbles: true,
cancelable: true
});
const capturedTransaction = editor.captureTransaction(() => {
view.someProp("handleKeyDown", (f) => f(view, event));
});
capturedTransaction == null ? void 0 : capturedTransaction.steps.forEach((step) => {
const newStep = step.map(tr.mapping);
if (newStep && dispatch) {
tr.maybeStep(newStep);
}
});
return true;
};
// src/commands/lift.ts
var import_commands7 = require("@tiptap/pm/commands");
// src/helpers/isNodeActive.ts
function isNodeActive(state, typeOrName, attributes = {}) {
const { from, to, empty } = state.selection;
const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
const nodeRanges = [];
state.doc.nodesBetween(from, to, (node, pos) => {
if (node.isText) {
return;
}
const relativeFrom = Math.max(from, pos);
const relativeTo = Math.min(to, pos + node.nodeSize);
nodeRanges.push({
node,
from: relativeFrom,
to: relativeTo
});
});
const selectionRange = to - from;
const matchedNodeRanges = nodeRanges.filter((nodeRange) => {
if (!type) {
return true;
}
return type.name === nodeRange.node.type.name;
}).filter((nodeRange) => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
if (empty) {
return !!matchedNodeRanges.length;
}
const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
return range >= selectionRange;
}
// src/commands/lift.ts
var lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema);
const isActive2 = isNodeActive(state, type, attributes);
if (!isActive2) {
return false;
}
return (0, import_commands7.lift)(state, dispatch);
};
// src/commands/liftEmptyBlock.ts
var import_commands8 = require("@tiptap/pm/commands");
var liftEmptyBlock = () => ({ state, dispatch }) => {
return (0, import_commands8.liftEmptyBlock)(state, dispatch);
};
// src/commands/liftListItem.ts
var import_schema_list = require("@tiptap/pm/schema-list");
var liftListItem = (typeOrName) => ({ state, dispatch }) => {
const type = getNodeType(typeOrName, state.schema);
return (0, import_schema_list.liftListItem)(type)(state, dispatch);
};
// src/commands/newlineInCode.ts
var import_commands9 = require("@tiptap/pm/commands");
var newlineInCode = () => ({ state, dispatch }) => {
return (0, import_commands9.newlineInCode)(state, dispatch);
};
// src/helpers/getSchemaTypeNameByName.ts
function getSchemaTypeNameByName(name, schema) {
if (schema.nodes[name]) {
return "node";
}
if (schema.marks[name]) {
return "mark";
}
return null;
}
// src/utilities/deleteProps.ts
function deleteProps(obj, propOrProps) {
const props = typeof propOrProps === "string" ? [propOrProps] : propOrProps;
return Object.keys(obj).reduce((newObj, prop) => {
if (!props.includes(prop)) {
newObj[prop] = obj[prop];
}
return newObj;
}, {});
}
// src/commands/resetAttributes.ts
var resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
let nodeType = null;
let markType = null;
const schemaType = getSchemaTypeNameByName(
typeof typeOrName === "string" ? typeOrName : typeOrName.name,
state.schema
);
if (!schemaType) {
return false;
}
if (schemaType === "node") {
nodeType = getNodeType(typeOrName, state.schema);
}
if (schemaType === "mark") {
markType = getMarkType(typeOrName, state.schema);
}
if (dispatch) {
tr.selection.ranges.forEach((range) => {
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
if (nodeType && nodeType === node.type) {
tr.setNodeMarkup(pos, void 0, deleteProps(node.attrs, attributes));
}
if (markType && node.marks.length) {
node.marks.forEach((mark) => {
if (markType === mark.type) {
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
}
});
}
});
});
}
return true;
};
// src/commands/scrollIntoView.ts
var scrollIntoView = () => ({ tr, dispatch }) => {
if (dispatch) {
tr.scrollIntoView();
}
return true;
};
// src/commands/selectAll.ts
var import_state6 = require("@tiptap/pm/state");
var selectAll = () => ({ tr, dispatch }) => {
if (dispatch) {
const selection = new import_state6.AllSelection(tr.doc);
tr.setSelection(selection);
}
return true;
};
// src/commands/selectNodeBackward.ts
var import_commands10 = require("@tiptap/pm/commands");
var selectNodeBackward = () => ({ state, dispatch }) => {
return (0, import_commands10.selectNodeBackward)(state, dispatch);
};
// src/commands/selectNodeForward.ts
var import_commands11 = require("@tiptap/pm/commands");
var selectNodeForward = () => ({ state, dispatch }) => {
return (0, import_commands11.selectNodeForward)(state, dispatch);
};
// src/commands/selectParentNode.ts
var import_commands12 = require("@tiptap/pm/commands");
var selectParentNode = () => ({ state, dispatch }) => {
return (0, import_commands12.selectParentNode)(state, dispatch);
};
// src/commands/selectTextblockEnd.ts
var import_commands13 = require("@tiptap/pm/commands");
var selectTextblockEnd = () => ({ state, dispatch }) => {
return (0, import_commands13.selectTextblockEnd)(state, dispatch);
};
// src/commands/selectTextblockStart.ts
var import_commands14 = require("@tiptap/pm/commands");
var selectTextblockStart = () => ({ state, dispatch }) => {
return (0, import_commands14.selectTextblockStart)(state, dispatch);
};
// src/helpers/createDocument.ts
function createDocument(content, schema, parseOptions = {}, options = {}) {
return createNodeFromContent(content, schema, {
slice: false,
parseOptions,
errorOnInvalidContent: options.errorOnInvalidContent
});
}
// src/commands/setContent.ts
var setContent = (content, { errorOnInvalidContent, emitUpdate = true, parseOptions = {} } = {}) => ({ editor, tr, dispatch, commands }) => {
const { doc } = tr;
if (parseOptions.preserveWhitespace !== "full") {
const document2 = createDocument(content, editor.schema, parseOptions, {
errorOnInvalidContent: errorOnInvalidContent != null ? errorOnInvalidContent : editor.options.enableContentCheck
});
if (dispatch) {
tr.replaceWith(0, doc.content.size, document2).setMeta("preventUpdate", !emitUpdate);
}
return true;
}
if (dispatch) {
tr.setMeta("preventUpdate", !emitUpdate);
}
return commands.insertContentAt({ from: 0, to: doc.content.size }, content, {
parseOptions,
errorOnInvalidContent: errorOnInvalidContent != null ? errorOnInvalidContent : editor.options.enableContentCheck
});
};
// src/helpers/getMarkAttributes.ts
function getMarkAttributes(state, typeOrName) {
const type = getMarkType(typeOrName, state.schema);
const { from, to, empty } = state.selection;
const marks = [];
if (empty) {
if (state.storedMarks) {
marks.push(...state.storedMarks);
}
marks.push(...state.selection.$head.marks());
} else {
state.doc.nodesBetween(from, to, (node) => {
marks.push(...node.marks);
});
}
const mark = marks.find((markItem) => markItem.type.name === type.name);
if (!mark) {
return {};
}
return { ...mark.attrs };
}
// src/helpers/combineTransactionSteps.ts
var import_transform5 = require("@tiptap/pm/transform");
function combineTransactionSteps(oldDoc, transactions) {
const transform = new import_transform5.Transform(oldDoc);
transactions.forEach((transaction) => {
transaction.steps.forEach((step) => {
transform.step(step);
});
});
return transform;
}
// src/helpers/defaultBlockAt.ts
function defaultBlockAt(match) {
for (let i = 0; i < match.edgeCount; i += 1) {
const { type } = match.edge(i);
if (type.isTextblock && !type.hasRequiredAttrs()) {
return type;
}
}
return null;
}
// src/helpers/findChildren.ts
function findChildren(node, predicate) {
const nodesWithPos = [];
node.descendants((child, pos) => {
if (predicate(child)) {
nodesWithPos.push({
node: child,
pos
});
}
});
return nodesWithPos;
}
// src/helpers/findChildrenInRange.ts
function findChildrenInRange(node, range, predicate) {
const nodesWithPos = [];
node.nodesBetween(range.from, range.to, (child, pos) => {
if (predicate(child)) {
nodesWithPos.push({
node: child,
pos
});
}
});
return nodesWithPos;
}
// src/helpers/findParentNodeClosestToPos.ts
function findParentNodeClosestToPos($pos, predicate) {
for (let i = $pos.depth; i > 0; i -= 1) {
const node = $pos.node(i);
if (predicate(node)) {
return {
pos: i > 0 ? $pos.before(i) : 0,
start: $pos.start(i),
depth: i,
node
};
}
}
}
// src/helpers/findParentNode.ts
function findParentNode(predicate) {
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
}
// src/helpers/getExtensionField.ts
function getExtensionField(extension, field, context) {
if (extension.config[field] === void 0 && extension.parent) {
return getExtensionField(extension.parent, field, context);
}
if (typeof extension.config[field] === "function") {
const value = extension.config[field].bind({
...context,
parent: extension.parent ? getExtensionField(extension.parent, field, context) : null
});
return value;
}
return extension.config[field];
}
// src/helpers/flattenExtensions.ts
function flattenExtensions(extensions) {
return extensions.map((extension) => {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage
};
const addExtensions = getExtensionField(extension, "addExtensions", context);
if (addExtensions) {
return [extension, ...flattenExtensions(addExtensions())];
}
return extension;
}).flat(10);
}
// src/helpers/generateHTML.ts
var import_model5 = require("@tiptap/pm/model");
// src/helpers/getHTMLFromFragment.ts
var import_model3 = require("@tiptap/pm/model");
function getHTMLFromFragment(fragment, schema) {
const documentFragment = import_model3.DOMSerializer.fromSchema(schema).serializeFragment(fragment);
const temporaryDocument = document.implementation.createHTMLDocument();
const container = temporaryDocument.createElement("div");
container.appendChild(documentFragment);
return container.innerHTML;
}
// src/helpers/getSchemaByResolvedExtensions.ts
var import_model4 = require("@tiptap/pm/model");
// src/utilities/isFunction.ts
function isFunction(value) {
return typeof value === "function";
}
// src/utilities/callOrReturn.ts
function callOrReturn(value, context = void 0, ...props) {
if (isFunction(value)) {
if (context) {
return value.bind(context)(...props);
}
return value(...props);
}
return value;
}
// src/utilities/isEmptyObject.ts
function isEmptyObject(value = {}) {
return Object.keys(value).length === 0 && value.constructor === Object;
}
// src/helpers/splitExtensions.ts
function splitExtensions(extensions) {
const baseExtensions = extensions.filter((extension) => extension.type === "extension");
const nodeExtensions = extensions.filter((extension) => extension.type === "node");
const markExtensions = extensions.filter((extension) => extension.type === "mark");
return {
baseExtensions,
nodeExtensions,
markExtensions
};
}
// src/helpers/getAttributesFromExtensions.ts
function getAttributesFromExtensions(extensions) {
const extensionAttributes = [];
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions];
const defaultAttribute = {
default: null,
validate: void 0,
rendered: true,
renderHTML: null,
parseHTML: null,
keepOnSplit: true,
isRequired: false
};
extensions.forEach((extension) => {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
extensions: nodeAndMarkExtensions
};
const addGlobalAttributes = getExtensionField(
extension,
"addGlobalAttributes",
context
);
if (!addGlobalAttributes) {
return;
}
const globalAttributes = addGlobalAttributes();
globalAttributes.forEach((globalAttribute) => {
globalAttribute.types.forEach((type) => {
Object.entries(globalAttribute.attributes).forEach(([name, attribute]) => {
extensionAttributes.push({
type,
name,
attribute: {
...defaultAttribute,
...attribute
}
});
});
});
});
});
nodeAndMarkExtensions.forEach((extension) => {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage
};
const addAttributes = getExtensionField(
extension,
"addAttributes",
context
);
if (!addAttributes) {
return;
}
const attributes = addAttributes();
Object.entries(attributes).forEach(([name, attribute]) => {
const mergedAttr = {
...defaultAttribute,
...attribute
};
if (typeof (mergedAttr == null ? void 0 : mergedAttr.default) === "function") {
mergedAttr.default = mergedAttr.default();
}
if ((mergedAttr == null ? void 0 : mergedAttr.isRequired) && (mergedAttr == null ? void 0 : mergedAttr.default) === void 0) {
delete mergedAttr.default;
}
extensionAttributes.push({
type: extension.name,
name,
attribute: mergedAttr
});
});
});
return extensionAttributes;
}
// src/utilities/mergeAttributes.ts
function mergeAttributes(...objects) {
return objects.filter((item) => !!item).reduce((items, item) => {
const mergedAttributes = { ...items };
Object.entries(item).forEach(([key, value]) => {
const exists = mergedAttributes[key];
if (!exists) {
mergedAttributes[key] = value;
return;
}
if (key === "class") {
const valueClasses = value ? String(value).split(" ") : [];
const existingClasses = mergedAttributes[key] ? mergedAttributes[key].split(" ") : [];
const insertClasses = valueClasses.filter((valueClass) => !existingClasses.includes(valueClass));
mergedAttributes[key] = [...existingClasses, ...insertClasses].join(" ");
} else if (key === "style") {
const newStyles = value ? value.split(";").map((style2) => style2.trim()).filter(Boolean) : [];
const existingStyles = mergedAttributes[key] ? mergedAttributes[key].split(";").map((style2) => style2.trim()).filter(Boolean) : [];
const styleMap = /* @__PURE__ */ new Map();
existingStyles.forEach((style2) => {
const [property, val] = style2.split(":").map((part) => part.trim());
styleMap.set(property, val);
});
newStyles.forEach((style2) => {
const [property, val] = style2.split(":").map((part) => part.trim());
styleMap.set(property, val);
});
mergedAttributes[key] = Array.from(styleMap.entries()).map(([property, val]) => `${property}: ${val}`).join("; ");
} else {
mergedAttributes[key] = value;
}
});
return mergedAttributes;
}, {});
}
// src/helpers/getRenderedAttributes.ts
function getRenderedAttributes(nodeOrMark, extensionAttributes) {
return extensionAttributes.filter((attribute) => attribute.type === nodeOrMark.type.name).filter((item) => item.attribute.rendered).map((item) => {
if (!item.attribute.renderHTML) {
return {
[item.name]: nodeOrMark.attrs[item.name