@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
1,441 lines • 74.1 kB
JavaScript
var Ie = Object.defineProperty;
var Be = (n, e, o) => e in n ? Ie(n, e, { enumerable: !0, configurable: !0, writable: !0, value: o }) : n[e] = o;
var b = (n, e, o) => Be(n, typeof e != "symbol" ? e + "" : e, o);
import { Plugin as T, PluginKey as P, NodeSelection as le, TextSelection as ce, Selection as N } from "prosemirror-state";
import { combineTransactionSteps as Te, getMarkRange as Pe, posToDOMRect as De, findChildren as j } from "@tiptap/core";
import Oe from "fast-deep-equal";
import { i as q, t as Ae, U as Me, n as Ne, g as R, a as L, c as de, m as Re, e as ue, f as Le, h as Ve, j as He, k as Fe, l as $e, o as W, p as G } from "./blockToNode-BNoNIXU7.js";
import { ai as he, a2 as J, aj as me, $ as Ue, a1 as Q } from "./defaultBlocks-Caw1U1oV.js";
import { c as C, a as H } from "./BlockNoteExtension-C2X7LW-V.js";
import { yCursorPlugin as _e, defaultSelectionBuilder as ze, ySyncPlugin as Ke, redoCommand as Ye, undoCommand as Xe, yUndoPlugin as je, yUndoPluginKey as Z } from "y-prosemirror";
import * as I from "yjs";
import { PluginKey as pe, Plugin as fe, TextSelection as qe } from "@tiptap/pm/state";
import { dropCursor as We } from "prosemirror-dropcursor";
import { redo as Ge, undo as Je, history as Qe } from "@tiptap/pm/history";
import { Decoration as A, DecorationSet as V } from "prosemirror-view";
import { v4 as Ze } from "uuid";
import { DOMParser as et, Slice as tt } from "@tiptap/pm/model";
import { DOMSerializer as ge, Fragment as we, Slice as ot } from "prosemirror-model";
import nt from "rehype-parse";
import rt from "rehype-remark";
import st from "remark-gfm";
import it from "remark-stringify";
import { unified as at } from "unified";
import { fromDom as lt } from "hast-util-from-dom";
import { visit as ct } from "unist-util-visit";
import { splitCell as dt, mergeCells as ut, deleteRow as ht, deleteColumn as mt, addRowBefore as pt, addRowAfter as ft, addColumnBefore as gt, addColumnAfter as wt, CellSelection as yt } from "prosemirror-tables";
function ye(n) {
const e = Array.from(n.classList).filter(
(o) => !o.startsWith("bn-")
) || [];
e.length > 0 ? n.className = e.join(" ") : n.removeAttribute("class");
}
function be(n, e, o, t) {
var a;
let r;
if (e)
if (typeof e == "string")
r = q([e], n.pmSchema);
else if (Array.isArray(e))
r = q(e, n.pmSchema);
else if (e.type === "tableContent")
r = Ae(e, n.pmSchema);
else
throw new Me(e.type);
else throw new Error("blockContent is required");
const i = ((t == null ? void 0 : t.document) ?? document).createDocumentFragment();
for (const c of r)
if (c.type.name !== "text" && n.schema.inlineContentSchema[c.type.name]) {
const l = n.schema.inlineContentSpecs[c.type.name].implementation;
if (l) {
const u = Ne(
c,
n.schema.inlineContentSchema,
n.schema.styleSchema
), h = l.toExternalHTML ? l.toExternalHTML(
u,
n
) : l.render.call(
{
renderType: "dom",
props: void 0
},
u,
() => {
},
n
);
if (h) {
if (i.appendChild(h.dom), h.contentDOM) {
const g = o.serializeFragment(
c.content,
t
);
h.contentDOM.dataset.editable = "", h.contentDOM.appendChild(g);
}
continue;
}
}
} else if (c.type.name === "text") {
let l = document.createTextNode(
c.textContent
);
for (const u of c.marks.toReversed())
if (u.type.name in n.schema.styleSpecs) {
const h = (n.schema.styleSpecs[u.type.name].implementation.toExternalHTML ?? n.schema.styleSpecs[u.type.name].implementation.render)(u.attrs.stringValue, n);
h.contentDOM.appendChild(l), l = h.dom;
} else {
const h = u.type.spec.toDOM(u, !0), g = ge.renderSpec(document, h);
g.contentDOM.appendChild(l), l = g.dom;
}
i.appendChild(l);
} else {
const l = o.serializeFragment(
we.from([c]),
t
);
i.appendChild(l);
}
return i.childNodes.length === 1 && ((a = i.firstChild) == null ? void 0 : a.nodeType) === 1 && ye(i.firstChild), i;
}
function bt(n, e, o, t, r, s, i, a) {
var w, y, v, D, _, z, K, Y, X;
const c = (a == null ? void 0 : a.document) ?? document, l = e.pmSchema.nodes.blockContainer, u = o.props || {};
for (const [k, x] of Object.entries(
e.schema.blockSchema[o.type].propSchema
))
!(k in u) && x.default !== void 0 && (u[k] = x.default);
const h = (y = (w = l.spec) == null ? void 0 : w.toDOM) == null ? void 0 : y.call(
w,
l.create({
id: o.id,
...u
})
), g = Array.from(h.dom.attributes), m = e.blockImplementations[o.type].implementation, p = ((v = m.toExternalHTML) == null ? void 0 : v.call(
{},
{ ...o, props: u },
e,
{
nestingLevel: i
}
)) || m.render.call(
{},
{ ...o, props: u },
e
), d = c.createDocumentFragment();
if (p.dom.classList.contains("bn-block-content")) {
const k = [
...g,
...Array.from(p.dom.attributes)
].filter(
(x) => x.name.startsWith("data") && x.name !== "data-content-type" && x.name !== "data-file-block" && x.name !== "data-node-view-wrapper" && x.name !== "data-node-type" && x.name !== "data-id" && x.name !== "data-editable"
);
for (const x of k)
p.dom.firstChild.setAttribute(x.name, x.value);
ye(p.dom.firstChild), i > 0 && p.dom.firstChild.setAttribute(
"data-nesting-level",
i.toString()
), d.append(...Array.from(p.dom.childNodes));
} else
d.append(p.dom), i > 0 && p.dom.setAttribute(
"data-nesting-level",
i.toString()
);
if (p.contentDOM && o.content) {
const k = be(
e,
o.content,
// TODO
t,
a
);
p.contentDOM.appendChild(k);
}
let f;
if (r.has(o.type) ? f = "OL" : s.has(o.type) && (f = "UL"), f) {
if (((D = n.lastChild) == null ? void 0 : D.nodeName) !== f) {
const k = c.createElement(f);
f === "OL" && "start" in u && u.start && (u == null ? void 0 : u.start) !== 1 && k.setAttribute("start", u.start + ""), n.append(k);
}
n.lastChild.appendChild(d);
} else
n.append(d);
if (o.children && o.children.length > 0) {
const k = c.createDocumentFragment();
if (ke(
k,
e,
o.children,
t,
r,
s,
i + 1,
a
), ((_ = n.lastChild) == null ? void 0 : _.nodeName) === "UL" || ((z = n.lastChild) == null ? void 0 : z.nodeName) === "OL")
for (; ((K = k.firstChild) == null ? void 0 : K.nodeName) === "UL" || ((Y = k.firstChild) == null ? void 0 : Y.nodeName) === "OL"; )
n.lastChild.lastChild.appendChild(k.firstChild);
e.pmSchema.nodes[o.type].isInGroup("blockContent") ? n.append(k) : (X = p.contentDOM) == null || X.append(k);
}
}
const ke = (n, e, o, t, r, s, i = 0, a) => {
for (const c of o)
bt(
n,
e,
c,
t,
r,
s,
i,
a
);
}, kt = (n, e, o, t, r, s) => {
const a = ((s == null ? void 0 : s.document) ?? document).createDocumentFragment();
return ke(
a,
n,
e,
o,
t,
r,
0,
s
), a;
}, Ce = (n, e) => {
const o = ge.fromSchema(n);
return {
exportBlocks: (t, r) => {
const s = kt(
e,
t,
o,
/* @__PURE__ */ new Set(["numberedListItem"]),
/* @__PURE__ */ new Set(["bulletListItem", "checkListItem", "toggleListItem"]),
r
), i = document.createElement("div");
return i.append(s), i.innerHTML;
},
exportInlineContent: (t, r) => {
const s = be(
e,
t,
o,
r
), i = document.createElement("div");
return i.append(s.cloneNode(!0)), i.innerHTML;
}
};
};
function Ct(n, e) {
if (e === 0)
return;
const o = n.resolve(e);
for (let t = o.depth; t > 0; t--) {
const r = o.node(t);
if (he(r))
return r.attrs.id;
}
}
function vt(n) {
return n.getMeta("paste") ? { type: "paste" } : n.getMeta("uiEvent") === "drop" ? { type: "drop" } : n.getMeta("history$") ? {
type: n.getMeta("history$").redo ? "redo" : "undo"
} : n.getMeta("y-sync$") ? n.getMeta("y-sync$").isUndoRedoOperation ? { type: "undo-redo" } : { type: "yjs-remote" } : { type: "local" };
}
function ee(n) {
const e = "__root__", o = {}, t = {}, r = R(n);
return n.descendants((s, i) => {
if (!he(s))
return !0;
const a = Ct(n, i), c = a ?? e;
t[c] || (t[c] = []);
const l = L(s, r);
return o[s.attrs.id] = { block: l, parentId: a }, t[c].push(s.attrs.id), !0;
}), { byId: o, childrenByParent: t };
}
function St(n, e) {
const o = /* @__PURE__ */ new Set();
if (!n || !e)
return o;
const t = new Set(n), r = e.filter((d) => t.has(d)), s = n.filter(
(d) => r.includes(d)
);
if (s.length <= 1 || r.length <= 1)
return o;
const i = {};
for (let d = 0; d < s.length; d++)
i[s[d]] = d;
const a = r.map((d) => i[d]), c = a.length, l = [], u = [], h = new Array(c).fill(-1), g = (d, f) => {
let w = 0, y = d.length;
for (; w < y; ) {
const v = w + y >>> 1;
d[v] < f ? w = v + 1 : y = v;
}
return w;
};
for (let d = 0; d < c; d++) {
const f = a[d], w = g(l, f);
w > 0 && (h[d] = u[w - 1]), w === l.length ? (l.push(f), u.push(d)) : (l[w] = f, u[w] = d);
}
const m = /* @__PURE__ */ new Set();
let p = u[u.length - 1] ?? -1;
for (; p !== -1; )
m.add(p), p = h[p];
for (let d = 0; d < r.length; d++)
m.has(d) || o.add(r[d]);
return o;
}
function xt(n, e = []) {
const o = vt(n), t = Te(n.before, [
n,
...e
]), r = ee(
t.before
), s = ee(
t.doc
), i = [], a = /* @__PURE__ */ new Set();
Object.keys(s.byId).filter((m) => !(m in r.byId)).forEach((m) => {
i.push({
type: "insert",
block: s.byId[m].block,
source: o,
prevBlock: void 0
}), a.add(m);
}), Object.keys(r.byId).filter((m) => !(m in s.byId)).forEach((m) => {
i.push({
type: "delete",
block: r.byId[m].block,
source: o,
prevBlock: void 0
}), a.add(m);
}), Object.keys(s.byId).filter((m) => m in r.byId).forEach((m) => {
var w, y;
const p = r.byId[m], d = s.byId[m];
p.parentId !== d.parentId ? (i.push({
type: "move",
block: d.block,
prevBlock: p.block,
source: o,
prevParent: p.parentId ? (w = r.byId[p.parentId]) == null ? void 0 : w.block : void 0,
currentParent: d.parentId ? (y = s.byId[d.parentId]) == null ? void 0 : y.block : void 0
}), a.add(m)) : Oe(
{ ...p.block, children: void 0 },
{ ...d.block, children: void 0 }
) || (i.push({
type: "update",
block: d.block,
prevBlock: p.block,
source: o
}), a.add(m));
});
const c = r.childrenByParent, l = s.childrenByParent, u = "__root__", h = /* @__PURE__ */ new Set([
...Object.keys(c),
...Object.keys(l)
]), g = /* @__PURE__ */ new Set();
return h.forEach((m) => {
const p = St(
c[m],
l[m]
);
p.size !== 0 && p.forEach((d) => {
var v, D;
const f = r.byId[d], w = s.byId[d];
!f || !w || f.parentId !== w.parentId || a.has(d) || (f.parentId ?? u) !== m || g.has(d) || (g.add(d), i.push({
type: "move",
block: w.block,
prevBlock: f.block,
source: o,
prevParent: f.parentId ? (v = r.byId[f.parentId]) == null ? void 0 : v.block : void 0,
currentParent: w.parentId ? (D = s.byId[w.parentId]) == null ? void 0 : D.block : void 0
}), a.add(d));
});
}), i;
}
const fo = C(() => {
const n = [];
return {
key: "blockChange",
prosemirrorPlugins: [
new T({
key: new P("blockChange"),
filterTransaction: (e) => {
let o;
return n.reduce((t, r) => t === !1 ? t : r({
getChanges() {
return o || (o = xt(e), o);
},
tr: e
}) !== !1, !0);
}
})
],
/**
* Subscribe to the block change events.
*/
subscribe(e) {
return n.push(e), () => {
n.splice(
n.indexOf(e),
1
);
};
}
};
});
function te(n) {
const e = n.charAt(0) === "#" ? n.substring(1, 7) : n, o = parseInt(e.substring(0, 2), 16), t = parseInt(e.substring(2, 4), 16), r = parseInt(e.substring(4, 6), 16), i = [o / 255, t / 255, r / 255].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
return 0.2126 * i[0] + 0.7152 * i[1] + 0.0722 * i[2] <= 0.179;
}
function Et(n) {
const e = document.createElement("span");
e.classList.add("bn-collaboration-cursor__base");
const o = document.createElement("span");
o.setAttribute("contentedEditable", "false"), o.classList.add("bn-collaboration-cursor__caret"), o.setAttribute(
"style",
`background-color: ${n.color}; color: ${te(n.color) ? "white" : "black"}`
);
const t = document.createElement("span");
return t.classList.add("bn-collaboration-cursor__label"), t.setAttribute(
"style",
`background-color: ${n.color}; color: ${te(n.color) ? "white" : "black"}`
), t.insertBefore(document.createTextNode(n.name), null), o.insertBefore(t, null), e.insertBefore(document.createTextNode(""), null), e.insertBefore(o, null), e.insertBefore(document.createTextNode(""), null), e;
}
const oe = C(
({ options: n }) => {
const e = /* @__PURE__ */ new Map(), o = n.provider && "awareness" in n.provider && typeof n.provider.awareness == "object" ? n.provider.awareness : void 0;
return o && ("setLocalStateField" in o && typeof o.setLocalStateField == "function" && o.setLocalStateField("user", n.user), "on" in o && typeof o.on == "function" && n.showCursorLabels !== "always" && o.on(
"change",
({
updated: t
}) => {
for (const r of t) {
const s = e.get(r);
s && (setTimeout(() => {
s.element.setAttribute("data-active", "");
}, 10), s.hideTimeout && clearTimeout(s.hideTimeout), e.set(r, {
element: s.element,
hideTimeout: setTimeout(() => {
s.element.removeAttribute("data-active");
}, 2e3)
}));
}
}
)), {
key: "yCursor",
prosemirrorPlugins: [
o ? _e(o, {
selectionBuilder: ze,
cursorBuilder(t, r) {
let s = e.get(r);
if (!s) {
const i = (n.renderCursor ?? Et)(t);
n.showCursorLabels !== "always" && (i.addEventListener("mouseenter", () => {
const a = e.get(r);
a.element.setAttribute("data-active", ""), a.hideTimeout && (clearTimeout(a.hideTimeout), e.set(r, {
element: a.element,
hideTimeout: void 0
}));
}), i.addEventListener("mouseleave", () => {
const a = e.get(r);
e.set(r, {
element: a.element,
hideTimeout: setTimeout(() => {
a.element.removeAttribute("data-active");
}, 2e3)
});
})), s = {
element: i,
hideTimeout: void 0
}, e.set(r, s);
}
return s.element;
}
}) : void 0
].filter(Boolean),
dependsOn: ["ySync"],
updateUser(t) {
o == null || o.setLocalStateField("user", t);
}
};
}
), F = C(
({ options: n }) => ({
key: "ySync",
prosemirrorPlugins: [Ke(n.fragment)],
runsBefore: ["default"]
})
), $ = C(() => ({
key: "yUndo",
prosemirrorPlugins: [je()],
dependsOn: ["yCursor", "ySync"],
undoCommand: Xe,
redoCommand: Ye
}));
function It(n, e) {
const o = n.doc;
if (n._item === null) {
const t = Array.from(o.share.keys()).find(
(r) => o.share.get(r) === n
);
if (t == null)
throw new Error("type does not exist in other ydoc");
return e.get(t, n.constructor);
} else {
const t = n._item, r = e.store.clients.get(t.id.client) ?? [], s = I.findIndexSS(r, t.id.clock);
return r[s].content.type;
}
}
const go = C(
({ editor: n, options: e }) => {
let o;
const t = H({ isForked: !1 });
return {
key: "yForkDoc",
store: t,
/**
* Fork the Y.js document from syncing to the remote,
* allowing modifications to the document without affecting the remote.
* These changes can later be rolled back or applied to the remote.
*/
fork() {
if (o)
return;
const r = e.fragment;
if (!r)
throw new Error("No fragment to fork from");
const s = new I.Doc();
I.applyUpdate(s, I.encodeStateAsUpdate(r.doc));
const i = It(r, s);
o = {
undoStack: Z.getState(n.prosemirrorState).undoManager.undoStack,
originalFragment: r,
forkedFragment: i
}, n.unregisterExtension([
$,
oe,
F
]);
const a = {
...e,
fragment: i
};
n.registerExtension([
F(a),
// No need to register the cursor plugin again, it's a local fork
$()
]), t.setState({ isForked: !0 });
},
/**
* Resume syncing the Y.js document to the remote
* If `keepChanges` is true, any changes that have been made to the forked document will be applied to the original document.
* Otherwise, the original document will be restored and the changes will be discarded.
*/
merge({ keepChanges: r }) {
if (!o)
return;
n.unregisterExtension(["ySync", "yCursor", "yUndo"]);
const { originalFragment: s, forkedFragment: i, undoStack: a } = o;
if (n.registerExtension([
F(e),
oe(e),
$()
]), Z.getState(
n.prosemirrorState
).undoManager.undoStack = a, r) {
const c = I.encodeStateAsUpdate(
i.doc,
I.encodeStateVector(s.doc)
);
I.applyUpdate(s.doc, c, n);
}
o = void 0, t.setState({ isForked: !1 });
}
};
}
), ve = (n, e) => {
e(n), n.forEach((o) => {
o instanceof I.XmlElement && ve(o, e);
});
}, Bt = (n, e) => {
const o = /* @__PURE__ */ new Map();
return n.forEach((t) => {
t instanceof I.XmlElement && ve(t, (r) => {
if (r.nodeName === "blockContainer" && r.hasAttribute("id")) {
const s = r.getAttribute("textColor"), i = r.getAttribute("backgroundColor"), a = {
textColor: s === J.textColor.default ? void 0 : s,
backgroundColor: i === J.backgroundColor.default ? void 0 : i
};
(a.textColor || a.backgroundColor) && o.set(r.getAttribute("id"), a);
}
});
}), o.size === 0 ? !1 : (e.doc.descendants((t, r) => {
if (t.type.name === "blockContainer" && o.has(t.attrs.id)) {
const s = e.doc.nodeAt(r + 1);
if (!s)
throw new Error("No element found");
e.setNodeMarkup(r + 1, void 0, {
// preserve existing attributes
...s.attrs,
// add the textColor and backgroundColor attributes
...o.get(t.attrs.id)
});
}
}), !0);
}, Tt = [Bt], wo = C(
({ options: n }) => {
let e = !1;
const o = new pe("schemaMigration");
return {
key: "schemaMigration",
prosemirrorPlugins: [
new fe({
key: o,
appendTransaction: (t, r, s) => {
if (e || // If any of the transactions are not due to a yjs sync, we don't need to run the migration
!t.some((a) => a.getMeta("y-sync$")) || // If none of the transactions result in a document change, we don't need to run the migration
t.every((a) => !a.docChanged) || // If the fragment is still empty, we can't run the migration (since it has not yet been applied to the Y.Doc)
!n.fragment.firstChild)
return;
const i = s.tr;
for (const a of Tt)
a(n.fragment, i);
if (e = !0, !!i.docChanged)
return i;
}
})
]
};
}
), yo = C(
({
editor: n,
options: e
}) => ({
key: "dropCursor",
prosemirrorPlugins: [
(e.dropCursor ?? We)({
width: 5,
color: "#ddeeff",
editor: n
})
]
})
), bo = C(({ editor: n }) => {
const e = H(!1), o = () => n.transact((t) => {
var s;
if (t.selection.empty || t.selection instanceof le && (t.selection.node.type.spec.content === "inline*" || ((s = t.selection.node.firstChild) == null ? void 0 : s.type.spec.content) === "inline*") || t.selection instanceof ce && t.doc.textBetween(t.selection.from, t.selection.to).length === 0)
return !1;
let r = !1;
return t.selection.content().content.descendants((i) => (i.type.spec.code && (r = !0), !r)), !r;
});
return {
key: "formattingToolbar",
store: e,
mount({ dom: t, signal: r }) {
let s = !1;
const i = n.onChange(() => {
s || e.setState(o());
}), a = n.onSelectionChange(() => {
s || e.setState(o());
});
t.addEventListener(
"pointerdown",
() => {
s = !0, e.setState(!1);
},
{ signal: r }
), n.prosemirrorView.root.addEventListener(
"pointerup",
() => {
s = !1, n.isFocused() && e.setState(o());
},
{ signal: r, capture: !0 }
), t.addEventListener(
"pointercancel",
() => {
s = !1;
},
{
signal: r,
capture: !0
}
), r.addEventListener("abort", () => {
i(), a();
});
}
};
}), ko = C(() => ({
key: "history",
prosemirrorPlugins: [Qe()],
undoCommand: Je,
redoCommand: Ge
})), Co = C(({ editor: n }) => {
function e(r) {
let s = n.prosemirrorView.nodeDOM(r);
for (; s && s.parentElement; ) {
if (s.nodeName === "A")
return s;
s = s.parentElement;
}
return null;
}
function o(r, s) {
return n.transact((i) => {
const a = i.doc.resolve(r), c = a.marks().find((u) => u.type.name === s);
if (!c)
return;
const l = Pe(a, c.type);
if (l)
return {
range: l,
mark: c,
get text() {
return i.doc.textBetween(l.from, l.to);
},
get position() {
return De(
n.prosemirrorView,
l.from,
l.to
).toJSON();
}
};
});
}
function t() {
return n.transact((r) => {
const s = r.selection;
if (s.empty)
return o(s.anchor, "link");
});
}
return {
key: "linkToolbar",
getLinkAtSelection: t,
getLinkElementAtPos: e,
getMarkAtPos: o,
getLinkAtElement(r) {
return n.transact(() => {
const s = n.prosemirrorView.posAtDOM(r, 0) + 1;
return o(s, "link");
});
},
editLink(r, s, i = n.transact((a) => a.selection.anchor)) {
n.transact((a) => {
const c = R(a), { range: l } = o(i + 1, "link") || {
range: {
from: a.selection.from,
to: a.selection.to
}
};
l && (a.insertText(s, l.from, l.to), a.addMark(
l.from,
l.from + s.length,
c.mark("link", { href: r })
));
}), n.prosemirrorView.focus();
},
deleteLink(r = n.transact((s) => s.selection.anchor)) {
n.transact((s) => {
const i = R(s), { range: a } = o(r + 1, "link") || {
range: {
from: s.selection.from,
to: s.selection.to
}
};
a && s.removeMark(a.from, a.to, i.marks.link).setMeta(
"preventAutolink",
!0
);
}), n.prosemirrorView.focus();
}
};
}), vo = [
"http",
"https",
"ftp",
"ftps",
"mailto",
"tel",
"callto",
"sms",
"cid",
"xmpp"
], So = "https", Pt = new P("node-selection-keyboard"), xo = C(
() => ({
key: "nodeSelectionKeyboard",
prosemirrorPlugins: [
new T({
key: Pt,
props: {
handleKeyDown: (n, e) => {
if ("node" in n.state.selection) {
if (e.ctrlKey || e.metaKey)
return !1;
if (e.key.length === 1)
return e.preventDefault(), !0;
if (e.key === "Enter" && !e.isComposing && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
const o = n.state.tr;
return n.dispatch(
o.insert(
n.state.tr.selection.$to.after(),
n.state.schema.nodes.paragraph.createChecked()
).setSelection(
new ce(
o.doc.resolve(
n.state.tr.selection.$to.after() + 1
)
)
)
), !0;
}
}
return !1;
}
}
})
]
})
), Dt = new P("blocknote-placeholder"), Eo = C(
({
editor: n,
options: e
}) => {
const o = e.placeholders;
return {
key: "placeholder",
prosemirrorPlugins: [
new T({
key: Dt,
view: (t) => {
const r = `placeholder-selector-${Ze()}`;
t.dom.classList.add(r);
const s = document.createElement("style"), i = n._tiptapEditor.options.injectNonce;
i && s.setAttribute("nonce", i), t.root instanceof window.ShadowRoot ? t.root.append(s) : t.root.head.appendChild(s);
const a = s.sheet, c = (l = "") => `.${r} .bn-block-content${l} .bn-inline-content:has(> .ProseMirror-trailingBreak:only-child):before`;
try {
const {
default: l,
emptyDocument: u,
...h
} = o || {};
for (const [p, d] of Object.entries(h)) {
const f = `[data-content-type="${p}"]`;
a.insertRule(
`${c(f)} { content: ${JSON.stringify(
d
)}; }`
);
}
const g = "[data-is-only-empty-block]", m = "[data-is-empty-and-focused]";
a.insertRule(
`${c(g)} { content: ${JSON.stringify(
u
)}; }`
), a.insertRule(
`${c(m)} { content: ${JSON.stringify(
l
)}; }`
);
} catch (l) {
console.warn(
"Failed to insert placeholder CSS rule - this is likely due to the browser not supporting certain CSS pseudo-element selectors (:has, :only-child:, or :before)",
l
);
}
return {
destroy: () => {
t.root instanceof window.ShadowRoot ? t.root.removeChild(s) : t.root.head.removeChild(s);
}
};
},
props: {
decorations: (t) => {
const { doc: r, selection: s } = t;
if (!n.isEditable || !s.empty || s.$from.parent.type.spec.code)
return;
const i = [];
t.doc.content.size === 6 && i.push(
A.node(2, 4, {
"data-is-only-empty-block": "true"
})
);
const a = s.$anchor, c = a.parent;
if (c.content.size === 0) {
const l = a.before();
i.push(
A.node(l, l + c.nodeSize, {
"data-is-empty-and-focused": "true"
})
);
}
return V.create(r, i);
}
}
})
]
};
}
), ne = new P("previous-blocks"), Ot = {
// Numbered List Items
index: "index",
// Headings
level: "level",
// All Blocks
type: "type",
depth: "depth",
"depth-change": "depth-change"
}, Io = C(() => {
let n;
return {
key: "previousBlockType",
prosemirrorPlugins: [
new T({
key: ne,
view(e) {
return {
update: async (o, t) => {
var r;
((r = this.key) == null ? void 0 : r.getState(o.state).updatedBlocks.size) > 0 && (n = setTimeout(() => {
o.dispatch(
o.state.tr.setMeta(ne, { clearUpdate: !0 })
);
}, 0));
},
destroy: () => {
n && clearTimeout(n);
}
};
},
state: {
init() {
return {
// Block attributes, by block ID, from just before the previous transaction.
prevTransactionOldBlockAttrs: {},
// Block attributes, by block ID, from just before the current transaction.
currentTransactionOldBlockAttrs: {},
// Set of IDs of blocks whose attributes changed from the current transaction.
updatedBlocks: /* @__PURE__ */ new Set()
};
},
apply(e, o, t, r) {
if (o.currentTransactionOldBlockAttrs = {}, o.updatedBlocks.clear(), !e.docChanged || t.doc.eq(r.doc))
return o;
const s = {}, i = j(
t.doc,
(l) => l.attrs.id
), a = new Map(
i.map((l) => [l.node.attrs.id, l])
), c = j(
r.doc,
(l) => l.attrs.id
);
for (const l of c) {
const u = a.get(l.node.attrs.id), h = u == null ? void 0 : u.node.firstChild, g = l.node.firstChild;
if (u && h && g) {
const m = {
index: g.attrs.index,
level: g.attrs.level,
type: g.type.name,
depth: r.doc.resolve(l.pos).depth
}, p = {
index: h.attrs.index,
level: h.attrs.level,
type: h.type.name,
depth: t.doc.resolve(u.pos).depth
};
s[l.node.attrs.id] = p, o.currentTransactionOldBlockAttrs[l.node.attrs.id] = p, JSON.stringify(p) !== JSON.stringify(m) && (p["depth-change"] = p.depth - m.depth, o.updatedBlocks.add(l.node.attrs.id));
}
}
return o.prevTransactionOldBlockAttrs = s, o;
}
},
props: {
decorations(e) {
const o = this.getState(e);
if (o.updatedBlocks.size === 0)
return;
const t = [];
return e.doc.descendants((r, s) => {
if (!r.attrs.id || !o.updatedBlocks.has(r.attrs.id))
return;
const i = o.currentTransactionOldBlockAttrs[r.attrs.id], a = {};
for (const [l, u] of Object.entries(i))
a["data-prev-" + Ot[l]] = u || "none";
const c = A.node(s, s + r.nodeSize, {
...a
});
t.push(c);
}), V.create(e.doc, t);
}
}
})
]
};
});
function Se(n, e) {
var o, t;
for (; n && n.parentElement && n.parentElement !== e.dom && ((o = n.getAttribute) == null ? void 0 : o.call(n, "data-node-type")) !== "blockContainer"; )
n = n.parentElement;
if (((t = n.getAttribute) == null ? void 0 : t.call(n, "data-node-type")) === "blockContainer")
return { node: n, id: n.getAttribute("data-id") };
}
function At() {
const n = (e) => {
let o = e.children.length;
for (let t = 0; t < o; t++) {
const r = e.children[t];
if (r.type === "element" && (n(r), r.tagName === "u"))
if (r.children.length > 0) {
e.children.splice(t, 1, ...r.children);
const s = r.children.length - 1;
o += s, t += s;
} else
e.children.splice(t, 1), o--, t--;
}
};
return n;
}
function Mt() {
const n = (e) => {
var o;
if (e.children && "length" in e.children && e.children.length)
for (let t = e.children.length - 1; t >= 0; t--) {
const r = e.children[t], s = t + 1 < e.children.length ? e.children[t + 1] : void 0;
r.type === "element" && r.tagName === "input" && ((o = r.properties) == null ? void 0 : o.type) === "checkbox" && (s == null ? void 0 : s.type) === "element" && s.tagName === "p" ? (s.tagName = "span", s.children.splice(
0,
0,
lt(document.createTextNode(" "))
)) : n(r);
}
};
return n;
}
function Nt() {
return (n) => {
ct(n, "element", (e, o, t) => {
var r, s, i, a;
if (t && e.tagName === "video") {
const c = ((r = e.properties) == null ? void 0 : r.src) || ((s = e.properties) == null ? void 0 : s["data-url"]) || "", l = ((i = e.properties) == null ? void 0 : i.title) || ((a = e.properties) == null ? void 0 : a["data-name"]) || "";
t.children[o] = {
type: "text",
value: ``
};
}
});
};
}
function xe(n) {
return at().use(nt, { fragment: !0 }).use(Nt).use(At).use(Mt).use(rt).use(st).use(it, {
handlers: { text: (o) => o.value }
}).processSync(n).value;
}
function Bo(n, e, o, t) {
const s = Ce(e, o).exportBlocks(n, t);
return xe(s);
}
function Rt(n) {
const e = [];
return n.descendants((o) => {
var r, s;
const t = R(o);
return o.type.name === "blockContainer" && ((r = o.firstChild) == null ? void 0 : r.type.name) === "blockGroup" ? !0 : o.type.name === "columnList" && o.childCount === 1 ? ((s = o.firstChild) == null || s.forEach((i) => {
e.push(L(i, t));
}), !1) : o.type.isInGroup("bnBlock") ? (e.push(L(o, t)), !1) : !0;
}), e;
}
class B extends N {
constructor(o, t) {
super(o, t);
b(this, "nodes");
const r = o.node();
this.nodes = [], o.doc.nodesBetween(o.pos, t.pos, (s, i, a) => {
if (a !== null && a.eq(r))
return this.nodes.push(s), !1;
});
}
static create(o, t, r = t) {
return new B(o.resolve(t), o.resolve(r));
}
content() {
return new ot(we.from(this.nodes), 0, 0);
}
eq(o) {
if (!(o instanceof B) || this.nodes.length !== o.nodes.length || this.from !== o.from || this.to !== o.to)
return !1;
for (let t = 0; t < this.nodes.length; t++)
if (!this.nodes[t].eq(o.nodes[t]))
return !1;
return !0;
}
map(o, t) {
const r = t.mapResult(this.from), s = t.mapResult(this.to);
return s.deleted ? N.near(o.resolve(r.pos)) : r.deleted ? N.near(o.resolve(s.pos)) : new B(
o.resolve(r.pos),
o.resolve(s.pos)
);
}
toJSON() {
return { type: "multiple-node", anchor: this.anchor, head: this.head };
}
}
N.jsonID("multiple-node", B);
let E;
function Lt(n, e) {
let o, t;
const r = e.resolve(n.from).node().type.spec.group === "blockContent", s = e.resolve(n.to).node().type.spec.group === "blockContent", i = Math.min(n.$anchor.depth, n.$head.depth);
if (r && s) {
const a = n.$from.start(i - 1), c = n.$to.end(i - 1);
o = e.resolve(a - 1).pos, t = e.resolve(c + 1).pos;
} else
o = n.from, t = n.to;
return { from: o, to: t };
}
function re(n, e, o = e) {
e === o && (o += n.state.doc.resolve(e + 1).node().nodeSize);
const t = n.domAtPos(e).node.cloneNode(!0), r = n.domAtPos(e).node, s = (h, g) => Array.prototype.indexOf.call(h.children, g), i = s(
r,
// Expects from position to be just before the first selected block.
n.domAtPos(e + 1).node.parentElement
), a = s(
r,
// Expects to position to be just after the last selected block.
n.domAtPos(o - 1).node.parentElement
);
for (let h = r.childElementCount - 1; h >= 0; h--)
(h > a || h < i) && t.removeChild(t.children[h]);
Ee(n.root), E = t;
const c = E.getElementsByTagName("iframe");
for (let h = 0; h < c.length; h++) {
const g = c[h], m = g.parentElement;
m && m.removeChild(g);
}
const u = n.dom.className.split(" ").filter(
(h) => h !== "ProseMirror" && h !== "bn-root" && h !== "bn-editor"
).join(" ");
E.className = E.className + " bn-drag-preview " + u, n.root instanceof ShadowRoot ? n.root.appendChild(E) : n.root.body.appendChild(E);
}
function Ee(n) {
E !== void 0 && (n instanceof ShadowRoot ? n.removeChild(E) : n.body.removeChild(E), E = void 0);
}
function Vt(n, e, o) {
if (!n.dataTransfer || o.headless)
return;
const t = o.prosemirrorView, r = me(e.id, t.state.doc);
if (!r)
throw new Error(`Block with ID ${e.id} not found`);
const s = r.posBeforeNode;
if (s != null) {
const i = t.state.selection, a = t.state.doc, { from: c, to: l } = Lt(i, a), u = c <= s && s < l, h = i.$anchor.node() !== i.$head.node() || i instanceof B;
u && h ? (t.dispatch(
t.state.tr.setSelection(B.create(a, c, l))
), re(t, c, l)) : (t.dispatch(
t.state.tr.setSelection(le.create(t.state.doc, s))
), re(t, s));
const g = t.state.selection.content(), m = o.pmSchema, p = t.serializeForClipboard(g).dom.innerHTML, d = Ce(m, o), f = Rt(g.content), w = d.exportBlocks(f, {}), y = xe(w);
n.dataTransfer.clearData(), n.dataTransfer.setData("blocknote/html", p), n.dataTransfer.setData("text/html", w), n.dataTransfer.setData("text/plain", y), n.dataTransfer.effectAllowed = "move", n.dataTransfer.setDragImage(E, 0, 0);
}
}
const se = 250;
function U(n, e, o = !0) {
const t = n.root.elementsFromPoint(e.left, e.top);
for (const r of t)
if (n.dom.contains(r))
return o && r.closest("[data-node-type=columnList]") ? U(
n,
{
// TODO can we do better than this?
left: e.left + 50,
// bit hacky, but if we're inside a column, offset x position to right to account for the width of sidemenu itself
top: e.top
},
!1
) : Se(r, n);
}
function Ht(n, e) {
if (!e.dom.firstChild)
return;
const o = e.dom.firstChild.getBoundingClientRect(), t = {
// Clamps the x position to the editor's bounding box.
left: Math.min(
Math.max(o.left + 10, n.x),
o.right - 10
),
top: n.y
}, r = U(e, t);
if (!r)
return;
const s = r.node.getBoundingClientRect();
return U(
e,
{
left: s.right - 10,
top: n.y
},
!1
);
}
class Ft {
constructor(e, o, t) {
b(this, "state");
b(this, "emitUpdate");
b(this, "mousePos");
b(this, "hoveredBlock");
b(this, "menuFrozen", !1);
b(this, "isDragOrigin", !1);
b(this, "updateState", (e) => {
this.state = e, this.emitUpdate(this.state);
});
b(this, "updateStateFromMousePos", () => {
var t, r, s, i, a;
if (this.menuFrozen || !this.mousePos)
return;
const e = this.findClosestEditorElement({
clientX: this.mousePos.x,
clientY: this.mousePos.y
});
if ((e == null ? void 0 : e.element) !== this.pmView.dom || e.distance > se) {
(t = this.state) != null && t.show && (this.state.show = !1, this.updateState(this.state));
return;
}
const o = Ht(this.mousePos, this.pmView);
if (!o || !this.editor.isEditable) {
(r = this.state) != null && r.show && (this.state.show = !1, this.updateState(this.state));
return;
}
if (!((s = this.state) != null && s.show && ((i = this.hoveredBlock) != null && i.hasAttribute("data-id")) && ((a = this.hoveredBlock) == null ? void 0 : a.getAttribute("data-id")) === o.id) && (this.hoveredBlock = o.node, this.editor.isEditable)) {
const c = o.node.getBoundingClientRect(), l = o.node.closest("[data-node-type=column]");
this.state = {
show: !0,
referencePos: new DOMRect(
l ? (
// We take the first child as column elements have some default
// padding. This is a little weird since this child element will
// be the first block, but since it's always non-nested and we
// only take the x coordinate, it's ok.
l.firstElementChild.getBoundingClientRect().x
) : this.pmView.dom.firstChild.getBoundingClientRect().x,
c.y,
c.width,
c.height
),
block: this.editor.getBlock(
this.hoveredBlock.getAttribute("data-id")
)
}, this.updateState(this.state);
}
});
/**
* If a block is being dragged, ProseMirror usually gets the context of what's
* being dragged from `view.dragging`, which is automatically set when a
* `dragstart` event fires in the editor. However, if the user tries to drag
* and drop blocks between multiple editors, only the one in which the drag
* began has that context, so we need to set it on the others manually. This
* ensures that PM always drops the blocks in between other blocks, and not
* inside them.
*
* After the `dragstart` event fires on the drag handle, it sets
* `blocknote/html` data on the clipboard. This handler fires right after,
* parsing the `blocknote/html` data into nodes and setting them on
* `view.dragging`.
*
* Note: Setting `view.dragging` on `dragover` would be better as the user
* could then drag between editors in different windows, but you can only
* access `dataTransfer` contents on `dragstart` and `drop` events.
*/
b(this, "onDragStart", (e) => {
var i;
const o = (i = e.dataTransfer) == null ? void 0 : i.getData("blocknote/html");
if (!o || this.pmView.dragging)
return;
const t = document.createElement("div");
t.innerHTML = o;
const s = et.fromSchema(this.pmView.state.schema).parse(t, {
topNode: this.pmView.state.schema.nodes.blockGroup.create()
});
this.pmView.dragging = {
slice: new tt(s.content, 0, 0),
move: !0
};
});
/**
* Finds the closest editor visually to the given coordinates
*/
b(this, "findClosestEditorElement", (e) => {
const o = Array.from(this.pmView.root.querySelectorAll(".bn-editor"));
if (o.length === 0)
return null;
let t = o[0], r = Number.MAX_VALUE;
return o.forEach((s) => {
const i = s.querySelector(".bn-block-group").getBoundingClientRect(), a = e.clientX < i.left ? i.left - e.clientX : e.clientX > i.right ? e.clientX - i.right : 0, c = e.clientY < i.top ? i.top - e.clientY : e.clientY > i.bottom ? e.clientY - i.bottom : 0, l = Math.sqrt(
Math.pow(a, 2) + Math.pow(c, 2)
);
l < r && (r = l, t = s);
}), {
element: t,
distance: r
};
});
/**
* This dragover event handler listens at the document level,
* and is trying to handle dragover events for all editors.
*
* It specifically is trying to handle the following cases:
* - If the dragover event is within the bounds of any editor, then it does nothing
* - If the dragover event is outside the bounds of any editor, but close enough (within DISTANCE_TO_CONSIDER_EDITOR_BOUNDS) to the closest editor,
* then it dispatches a synthetic dragover event to the closest editor (which will trigger the drop-cursor to be shown on that editor)
* - If the dragover event is outside the bounds of the current editor, then it will dispatch a synthetic dragleave event to the current editor
* (which will trigger the drop-cursor to be removed from the current editor)
*
* The synthetic event is a necessary evil because we do not control prosemirror-dropcursor to be able to show the drop-cursor within the range we want
*/
b(this, "onDragOver", (e) => {
var r;
if (e.synthetic || !(this.pmView.dragging !== null || this.isDragOrigin || ((r = e.dataTransfer) == null ? void 0 : r.types.includes("blocknote/html")) || e.target instanceof Node && this.pmView.dom.contains(e.target)))
return;
const t = this.getDragEventContext(e);
if (!t || !t.isDropPoint) {
this.closeDropCursor();
return;
}
t.isDropPoint && !t.isDropWithinEditorBounds && this.dispatchSyntheticEvent(e);
});
/**
* Closes the drop-cursor for the current editor
*/
b(this, "closeDropCursor", () => {
const e = new Event("dragleave", { bubbles: !1 });
e.synthetic = !0, this.pmView.dom.dispatchEvent(e);
});
/**
* It is surprisingly difficult to determine the information we need to know about a drag event
*
* This function is trying to determine the following:
* - Whether the current editor instance is the drop point
* - Whether the current editor instance is the drag origin
* - Whether the drop event is within the bounds of the current editor instance
*/
b(this, "getDragEventContext", (e) => {
var l, u;
if (!(this.pmView.dragging !== null || this.isDragOrigin || ((l = e.dataTransfer) == null ? void 0 : l.types.includes("blocknote/html")) || e.target instanceof Node && this.pmView.dom.contains(e.target)))
return;
const t = !((u = e.dataTransfer) != null && u.types.includes("blocknote/html")) && !!this.pmView.dragging, r = !!this.isDragOrigin, s = t || r, i = this.findClosestEditorElement(e);
if (!i || i.distance > se)
return;
const a = i.element === this.pmView.dom, c = a && i.distance === 0;
if (!(!a && !s))
return {
isDropPoint: a,
isDropWithinEditorBounds: c,
isDragOrigin: s
};
});
/**
* The drop event handler listens at the document level,
* and handles drop events for all editors.
*
* It specifically handles the following cases:
* - If we are both the drag origin and drop point:
* - Let normal drop handling take over
* - If we are the drop point but not the drag origin:
* - Collapse selection to prevent PM from deleting unrelated content
* - If drop event is outside our editor bounds, dispatch synthetic drop event to our editor
* - If we are the drag origin but not the drop point:
* - Delete the dragged content from our editor after a delay
*/
b(this, "onDrop", (e) => {
var a;
if (e.synthetic || !(this.pmView.dragging !== null || this.isDragOrigin || ((a = e.dataTransfer) == null ? void 0 : a.types.includes("blocknote/html")) || e.target instanceof Node && this.pmView.dom.contains(e.target)))
return;
const t = this.getDragEventContext(e);
if (!t) {
this.closeDropCursor();
return;
}
const { isDropPoint: r, isDropWithinEditorBounds: s, isDragOrigin: i } = t;
if (!s && r && this.dispatchSyntheticEvent(e), r) {
if (this.pmView.dragging)
return;
this.pmView.dispatch(
this.pmView.state.tr.setSelection(
qe.create(
this.pmView.state.tr.doc,
this.pmView.state.tr.selection.anchor
)
)
);
return;
} else if (i) {
setTimeout(
() => this.pmView.dispatch(this.pmView.state.tr.deleteSelection()),
0
);
return;
}
});
b(this, "onDragEnd", (e) => {
e.synthetic || (this.pmView.dragging = null);
});
b(this, "onKeyDown", (e) => {
var o;
(o = this.state) != null && o.show && this.editor.isFocused() && (this.state.show = !1, this.emitUpdate(this.state));
});
b(this, "onMouseMove", (e) => {
var s;
if (this.menuFrozen)
return;
this.mousePos = { x: e.clientX, y: e.clientY };
const o = this.pmView.dom.getBoundingClientRect(), t = this.mousePos.x > o.left && this.mousePos.x < o.right && this.mousePos.y > o.top && this.mousePos.y < o.bottom, r = this.pmView.dom.parentElement;
if (
// Cursor is within the editor area
t && // An element is hovered
e && e.target && // Element is outside the editor
!(r === e.target || r.contains(e.target))
) {
(s = this.state) != null && s.show && (this.state.show = !1, this.emitUpdate(this.state));
return;
}
this.updateStateFromMousePos();
});
this.editor = e, this.pmView = o, this.emitUpdate = () => {
if (!this.state)
throw new Error("Attempting to update uninitialized side menu");
t(this.state);
}, this.pmView.root.addEventListener(
"dragstart",
this.onDragStart
), this.pmView.root.addEventListener(
"dragover",
this.onDragOver
), this.pmView.root.addEventListener(
"drop",
this.onDrop,
!0
), this.pmView.root.addEventListener(
"dragend",
this.onDragEnd,
!0
), this.pmView.root.addEventListener(
"mousemove",
this.onMouseMove,
!0
), this.pmView.root.addEventListener(
"keydown",
this.onKeyDown,
!0
);
}
dispatchSyntheticEvent(e) {
const o = new Event(e.type, e), t = this.pmView.dom.firstChild.getBoundingClientRect();
o.clientX = e.clientX, o.clientY = e.clientY, o.clientX = Math.min(
Math.max(e.clientX, t.left),
t.left + t.width
), o.clientY = Math.min(
Math.max(e.clientY, t.top),
t.top + t.height
), o.dataTransfer = e.dataTransfer, o.preventDefault = () => e.preventDefault(), o.synthetic = !0, this.pmView.dom.dispatchEvent(o);
}
// Needed in cases where the editor state updates without the mouse cursor
// moving, as some state updates can require a side menu update. For example,
// adding a button to the side menu which removes the block can cause the
// block below to jump up into the place of the removed block when clicked,
// allowing