@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
1,451 lines • 72.8 kB
JavaScript
var Ee = Object.defineProperty;
var Ie = (n, e, t) => e in n ? Ee(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
var b = (n, e, t) => Ie(n, typeof e != "symbol" ? e + "" : e, t);
import { Plugin as T, PluginKey as P, NodeSelection as ae, TextSelection as le, Selection as L } from "prosemirror-state";
import { combineTransactionSteps as Be, getMarkRange as Te, posToDOMRect as Pe, findChildren as X } from "@tiptap/core";
import Oe from "fast-deep-equal";
import { i as j, t as De, U as Ae, n as Me, g as N, a as R, c as ce, m as Le, e as de, f as Ne, h as Re, j as Ve, k as He, l as Fe, o as q, p as W } from "./blockToNode-BNoNIXU7.js";
import { ai as ue, a2 as G, aj as he, $ as $e, a1 as J } from "./defaultBlocks-DvCGYzqu.js";
import { c as k, a as H } from "./BlockNoteExtension-C2X7LW-V.js";
import { yCursorPlugin as Ue, defaultSelectionBuilder as _e, ySyncPlugin as ze, redoCommand as Ke, undoCommand as Ye, yUndoPlugin as Xe, yUndoPluginKey as Q } from "y-prosemirror";
import * as I from "yjs";
import { PluginKey as me, Plugin as pe, TextSelection as je } from "@tiptap/pm/state";
import { dropCursor as qe } from "prosemirror-dropcursor";
import { redo as We, undo as Ge, history as Je } from "@tiptap/pm/history";
import { Decoration as A, DecorationSet as V } from "prosemirror-view";
import { v4 as Qe } from "uuid";
import { DOMParser as Ze, Slice as et } from "@tiptap/pm/model";
import { DOMSerializer as fe, Fragment as ge, Slice as tt } from "prosemirror-model";
import ot from "rehype-parse";
import nt from "rehype-remark";
import rt from "remark-gfm";
import st from "remark-stringify";
import { unified as it } from "unified";
import { fromDom as at } from "hast-util-from-dom";
import { visit as lt } from "unist-util-visit";
import { splitCell as ct, mergeCells as dt, deleteRow as ut, deleteColumn as ht, addRowBefore as mt, addRowAfter as pt, addColumnBefore as ft, addColumnAfter as gt, CellSelection as wt } from "prosemirror-tables";
function we(n) {
const e = Array.from(n.classList).filter(
(t) => !t.startsWith("bn-")
) || [];
e.length > 0 ? n.className = e.join(" ") : n.removeAttribute("class");
}
function ye(n, e, t, o) {
var a;
let r;
if (e)
if (typeof e == "string")
r = j([e], n.pmSchema);
else if (Array.isArray(e))
r = j(e, n.pmSchema);
else if (e.type === "tableContent")
r = De(e, n.pmSchema);
else
throw new Ae(e.type);
else throw new Error("blockContent is required");
const i = ((o == null ? void 0 : o.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 m = Me(
c,
n.schema.inlineContentSchema,
n.schema.styleSchema
), h = l.toExternalHTML ? l.toExternalHTML(
m,
n
) : l.render.call(
{
renderType: "dom",
props: void 0
},
m,
() => {
},
n
);
if (h) {
if (i.appendChild(h.dom), h.contentDOM) {
const f = t.serializeFragment(
c.content,
o
);
h.contentDOM.dataset.editable = "", h.contentDOM.appendChild(f);
}
continue;
}
}
} else if (c.type.name === "text") {
let l = document.createTextNode(
c.textContent
);
for (const m of c.marks.toReversed())
if (m.type.name in n.schema.styleSpecs) {
const h = (n.schema.styleSpecs[m.type.name].implementation.toExternalHTML ?? n.schema.styleSpecs[m.type.name].implementation.render)(m.attrs.stringValue, n);
h.contentDOM.appendChild(l), l = h.dom;
} else {
const h = m.type.spec.toDOM(m, !0), f = fe.renderSpec(document, h);
f.contentDOM.appendChild(l), l = f.dom;
}
i.appendChild(l);
} else {
const l = t.serializeFragment(
ge.from([c]),
o
);
i.appendChild(l);
}
return i.childNodes.length === 1 && ((a = i.firstChild) == null ? void 0 : a.nodeType) === 1 && we(i.firstChild), i;
}
function yt(n, e, t, o, r, s, i) {
var p, w, y, E, O, _, z, K, Y;
const a = (i == null ? void 0 : i.document) ?? document, c = e.pmSchema.nodes.blockContainer, l = t.props || {};
for (const [v, S] of Object.entries(
e.schema.blockSchema[t.type].propSchema
))
!(v in l) && S.default !== void 0 && (l[v] = S.default);
const m = (w = (p = c.spec) == null ? void 0 : p.toDOM) == null ? void 0 : w.call(
p,
c.create({
id: t.id,
...l
})
), h = Array.from(m.dom.attributes), f = e.blockImplementations[t.type].implementation, u = ((y = f.toExternalHTML) == null ? void 0 : y.call(
{},
{ ...t, props: l },
e
)) || f.render.call(
{},
{ ...t, props: l },
e
), g = a.createDocumentFragment();
if (u.dom.classList.contains("bn-block-content")) {
const v = [
...h,
...Array.from(u.dom.attributes)
].filter(
(S) => S.name.startsWith("data") && S.name !== "data-content-type" && S.name !== "data-file-block" && S.name !== "data-node-view-wrapper" && S.name !== "data-node-type" && S.name !== "data-id" && S.name !== "data-editable"
);
for (const S of v)
u.dom.firstChild.setAttribute(S.name, S.value);
we(u.dom.firstChild), g.append(...Array.from(u.dom.childNodes));
} else
g.append(u.dom);
if (u.contentDOM && t.content) {
const v = ye(
e,
t.content,
// TODO
o,
i
);
u.contentDOM.appendChild(v);
}
let d;
if (r.has(t.type) ? d = "OL" : s.has(t.type) && (d = "UL"), d) {
if (((E = n.lastChild) == null ? void 0 : E.nodeName) !== d) {
const v = a.createElement(d);
d === "OL" && "start" in l && l.start && (l == null ? void 0 : l.start) !== 1 && v.setAttribute("start", l.start + ""), n.append(v);
}
n.lastChild.appendChild(g);
} else
n.append(g);
if (t.children && t.children.length > 0) {
const v = a.createDocumentFragment();
if (be(
v,
e,
t.children,
o,
r,
s,
i
), ((O = n.lastChild) == null ? void 0 : O.nodeName) === "UL" || ((_ = n.lastChild) == null ? void 0 : _.nodeName) === "OL")
for (; ((z = v.firstChild) == null ? void 0 : z.nodeName) === "UL" || ((K = v.firstChild) == null ? void 0 : K.nodeName) === "OL"; )
n.lastChild.lastChild.appendChild(v.firstChild);
e.pmSchema.nodes[t.type].isInGroup("blockContent") ? n.append(v) : (Y = u.contentDOM) == null || Y.append(v);
}
}
const be = (n, e, t, o, r, s, i) => {
for (const a of t)
yt(
n,
e,
a,
o,
r,
s,
i
);
}, bt = (n, e, t, o, r, s) => {
const a = ((s == null ? void 0 : s.document) ?? document).createDocumentFragment();
return be(
a,
n,
e,
t,
o,
r,
s
), a;
}, ve = (n, e) => {
const t = fe.fromSchema(n);
return {
exportBlocks: (o, r) => {
const s = bt(
e,
o,
t,
/* @__PURE__ */ new Set(["numberedListItem"]),
/* @__PURE__ */ new Set(["bulletListItem", "checkListItem", "toggleListItem"]),
r
), i = document.createElement("div");
return i.append(s), i.innerHTML;
},
exportInlineContent: (o, r) => {
const s = ye(
e,
o,
t,
r
), i = document.createElement("div");
return i.append(s.cloneNode(!0)), i.innerHTML;
}
};
};
function vt(n, e) {
if (e === 0)
return;
const t = n.resolve(e);
for (let o = t.depth; o > 0; o--) {
const r = t.node(o);
if (ue(r))
return r.attrs.id;
}
}
function kt(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 Z(n) {
const e = "__root__", t = {}, o = {}, r = N(n);
return n.descendants((s, i) => {
if (!ue(s))
return !0;
const a = vt(n, i), c = a ?? e;
o[c] || (o[c] = []);
const l = R(s, r);
return t[s.attrs.id] = { block: l, parentId: a }, o[c].push(s.attrs.id), !0;
}), { byId: t, childrenByParent: o };
}
function Ct(n, e) {
const t = /* @__PURE__ */ new Set();
if (!n || !e)
return t;
const o = new Set(n), r = e.filter((d) => o.has(d)), s = n.filter(
(d) => r.includes(d)
);
if (s.length <= 1 || r.length <= 1)
return t;
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 = [], m = [], h = new Array(c).fill(-1), f = (d, p) => {
let w = 0, y = d.length;
for (; w < y; ) {
const E = w + y >>> 1;
d[E] < p ? w = E + 1 : y = E;
}
return w;
};
for (let d = 0; d < c; d++) {
const p = a[d], w = f(l, p);
w > 0 && (h[d] = m[w - 1]), w === l.length ? (l.push(p), m.push(d)) : (l[w] = p, m[w] = d);
}
const u = /* @__PURE__ */ new Set();
let g = m[m.length - 1] ?? -1;
for (; g !== -1; )
u.add(g), g = h[g];
for (let d = 0; d < r.length; d++)
u.has(d) || t.add(r[d]);
return t;
}
function St(n, e = []) {
const t = kt(n), o = Be(n.before, [
n,
...e
]), r = Z(
o.before
), s = Z(
o.doc
), i = [], a = /* @__PURE__ */ new Set();
Object.keys(s.byId).filter((u) => !(u in r.byId)).forEach((u) => {
i.push({
type: "insert",
block: s.byId[u].block,
source: t,
prevBlock: void 0
}), a.add(u);
}), Object.keys(r.byId).filter((u) => !(u in s.byId)).forEach((u) => {
i.push({
type: "delete",
block: r.byId[u].block,
source: t,
prevBlock: void 0
}), a.add(u);
}), Object.keys(s.byId).filter((u) => u in r.byId).forEach((u) => {
var w, y;
const g = r.byId[u], d = s.byId[u];
g.parentId !== d.parentId ? (i.push({
type: "move",
block: d.block,
prevBlock: g.block,
source: t,
prevParent: g.parentId ? (w = r.byId[g.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(u)) : Oe(
{ ...g.block, children: void 0 },
{ ...d.block, children: void 0 }
) || (i.push({
type: "update",
block: d.block,
prevBlock: g.block,
source: t
}), a.add(u));
});
const c = r.childrenByParent, l = s.childrenByParent, m = "__root__", h = /* @__PURE__ */ new Set([
...Object.keys(c),
...Object.keys(l)
]), f = /* @__PURE__ */ new Set();
return h.forEach((u) => {
const g = Ct(
c[u],
l[u]
);
g.size !== 0 && g.forEach((d) => {
var E, O;
const p = r.byId[d], w = s.byId[d];
!p || !w || p.parentId !== w.parentId || a.has(d) || (p.parentId ?? m) !== u || f.has(d) || (f.add(d), i.push({
type: "move",
block: w.block,
prevBlock: p.block,
source: t,
prevParent: p.parentId ? (E = r.byId[p.parentId]) == null ? void 0 : E.block : void 0,
currentParent: w.parentId ? (O = s.byId[w.parentId]) == null ? void 0 : O.block : void 0
}), a.add(d));
});
}), i;
}
const po = k(() => {
const n = [];
return {
key: "blockChange",
prosemirrorPlugins: [
new T({
key: new P("blockChange"),
filterTransaction: (e) => {
let t;
return n.reduce((o, r) => o === !1 ? o : r({
getChanges() {
return t || (t = St(e), t);
},
tr: e
}) !== !1, !0);
}
})
],
/**
* Subscribe to the block change events.
*/
subscribe(e) {
return n.push(e), () => {
n.splice(
n.indexOf(e),
1
);
};
}
};
});
function ee(n) {
const e = n.charAt(0) === "#" ? n.substring(1, 7) : n, t = parseInt(e.substring(0, 2), 16), o = parseInt(e.substring(2, 4), 16), r = parseInt(e.substring(4, 6), 16), i = [t / 255, o / 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 xt(n) {
const e = document.createElement("span");
e.classList.add("bn-collaboration-cursor__base");
const t = document.createElement("span");
t.setAttribute("contentedEditable", "false"), t.classList.add("bn-collaboration-cursor__caret"), t.setAttribute(
"style",
`background-color: ${n.color}; color: ${ee(n.color) ? "white" : "black"}`
);
const o = document.createElement("span");
return o.classList.add("bn-collaboration-cursor__label"), o.setAttribute(
"style",
`background-color: ${n.color}; color: ${ee(n.color) ? "white" : "black"}`
), o.insertBefore(document.createTextNode(n.name), null), t.insertBefore(o, null), e.insertBefore(document.createTextNode(""), null), e.insertBefore(t, null), e.insertBefore(document.createTextNode(""), null), e;
}
const te = k(
({ options: n }) => {
const e = /* @__PURE__ */ new Map(), t = n.provider && "awareness" in n.provider && typeof n.provider.awareness == "object" ? n.provider.awareness : void 0;
return t && ("setLocalStateField" in t && typeof t.setLocalStateField == "function" && t.setLocalStateField("user", n.user), "on" in t && typeof t.on == "function" && n.showCursorLabels !== "always" && t.on(
"change",
({
updated: o
}) => {
for (const r of o) {
const s = e.get(r);
s && (s.element.setAttribute("data-active", ""), s.hideTimeout && clearTimeout(s.hideTimeout), e.set(r, {
element: s.element,
hideTimeout: setTimeout(() => {
s.element.removeAttribute("data-active");
}, 2e3)
}));
}
}
)), {
key: "yCursor",
prosemirrorPlugins: [
t ? Ue(t, {
selectionBuilder: _e,
cursorBuilder(o, r) {
let s = e.get(r);
if (!s) {
const i = (n.renderCursor ?? xt)(o);
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(o) {
t == null || t.setLocalStateField("user", o);
}
};
}
), F = k(
({ options: n }) => ({
key: "ySync",
prosemirrorPlugins: [ze(n.fragment)],
runsBefore: ["default"]
})
), $ = k(() => ({
key: "yUndo",
prosemirrorPlugins: [Xe()],
dependsOn: ["yCursor", "ySync"],
undoCommand: Ye,
redoCommand: Ke
}));
function Et(n, e) {
const t = n.doc;
if (n._item === null) {
const o = Array.from(t.share.keys()).find(
(r) => t.share.get(r) === n
);
if (o == null)
throw new Error("type does not exist in other ydoc");
return e.get(o, n.constructor);
} else {
const o = n._item, r = e.store.clients.get(o.id.client) ?? [], s = I.findIndexSS(r, o.id.clock);
return r[s].content.type;
}
}
const fo = k(
({ editor: n, options: e }) => {
let t;
const o = H({ isForked: !1 });
return {
key: "yForkDoc",
store: o,
/**
* 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 (t)
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 = Et(r, s);
t = {
undoStack: Q.getState(n.prosemirrorState).undoManager.undoStack,
originalFragment: r,
forkedFragment: i
}, n.unregisterExtension([
$,
te,
F
]);
const a = {
...e,
fragment: i
};
n.registerExtension([
F(a),
// No need to register the cursor plugin again, it's a local fork
$()
]), o.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 (!t)
return;
n.unregisterExtension(["ySync", "yCursor", "yUndo"]);
const { originalFragment: s, forkedFragment: i, undoStack: a } = t;
if (n.registerExtension([
F(e),
te(e),
$()
]), Q.getState(
n.prosemirrorState
).undoManager.undoStack = a, r) {
const c = I.encodeStateAsUpdate(
i.doc,
I.encodeStateVector(s.doc)
);
I.applyUpdate(s.doc, c, n);
}
t = void 0, o.setState({ isForked: !1 });
}
};
}
), ke = (n, e) => {
e(n), n.forEach((t) => {
t instanceof I.XmlElement && ke(t, e);
});
}, It = (n, e) => {
const t = /* @__PURE__ */ new Map();
return n.forEach((o) => {
o instanceof I.XmlElement && ke(o, (r) => {
if (r.nodeName === "blockContainer" && r.hasAttribute("id")) {
const s = r.getAttribute("textColor"), i = r.getAttribute("backgroundColor"), a = {
textColor: s === G.textColor.default ? void 0 : s,
backgroundColor: i === G.backgroundColor.default ? void 0 : i
};
(a.textColor || a.backgroundColor) && t.set(r.getAttribute("id"), a);
}
});
}), t.size === 0 ? !1 : (e.doc.descendants((o, r) => {
if (o.type.name === "blockContainer" && t.has(o.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
...t.get(o.attrs.id)
});
}
}), !0);
}, Bt = [It], go = k(
({ options: n }) => {
let e = !1;
const t = new me("schemaMigration");
return {
key: "schemaMigration",
prosemirrorPlugins: [
new pe({
key: t,
appendTransaction: (o, r, s) => {
if (e || // If any of the transactions are not due to a yjs sync, we don't need to run the migration
!o.some((a) => a.getMeta("y-sync$")) || // If none of the transactions result in a document change, we don't need to run the migration
o.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 Bt)
a(n.fragment, i);
if (e = !0, !!i.docChanged)
return i;
}
})
]
};
}
), wo = k(
({
editor: n,
options: e
}) => ({
key: "dropCursor",
prosemirrorPlugins: [
(e.dropCursor ?? qe)({
width: 5,
color: "#ddeeff",
editor: n
})
]
})
), yo = k(({ editor: n }) => {
const e = H(!1), t = () => n.transact((o) => {
var s;
if (o.selection.empty || o.selection instanceof ae && (o.selection.node.type.spec.content === "inline*" || ((s = o.selection.node.firstChild) == null ? void 0 : s.type.spec.content) === "inline*") || o.selection instanceof le && o.doc.textBetween(o.selection.from, o.selection.to).length === 0)
return !1;
let r = !1;
return o.selection.content().content.descendants((i) => (i.type.spec.code && (r = !0), !r)), !r;
});
return {
key: "formattingToolbar",
store: e,
mount({ dom: o, signal: r }) {
let s = !1;
const i = n.onChange(() => {
s || e.setState(t());
}), a = n.onSelectionChange(() => {
s || e.setState(t());
});
o.addEventListener(
"pointerdown",
() => {
s = !0, e.setState(!1);
},
{ signal: r }
), n.prosemirrorView.root.addEventListener(
"pointerup",
() => {
s = !1, n.isFocused() && e.setState(t());
},
{ signal: r, capture: !0 }
), o.addEventListener(
"pointercancel",
() => {
s = !1;
},
{
signal: r,
capture: !0
}
), r.addEventListener("abort", () => {
i(), a();
});
}
};
}), bo = k(() => ({
key: "history",
prosemirrorPlugins: [Je()],
undoCommand: Ge,
redoCommand: We
})), vo = k(({ 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 t(r, s) {
return n.transact((i) => {
const a = i.doc.resolve(r), c = a.marks().find((m) => m.type.name === s);
if (!c)
return;
const l = Te(a, c.type);
if (l)
return {
range: l,
mark: c,
get text() {
return i.doc.textBetween(l.from, l.to);
},
get position() {
return Pe(
n.prosemirrorView,
l.from,
l.to
).toJSON();
}
};
});
}
function o() {
return n.transact((r) => {
const s = r.selection;
if (s.empty)
return t(s.anchor, "link");
});
}
return {
key: "linkToolbar",
getLinkAtSelection: o,
getLinkElementAtPos: e,
getMarkAtPos: t,
getLinkAtElement(r) {
return n.transact(() => {
const s = n.prosemirrorView.posAtDOM(r, 0) + 1;
return t(s, "link");
});
},
editLink(r, s, i = n.transact((a) => a.selection.anchor)) {
n.transact((a) => {
const c = N(a), { range: l } = t(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 = N(s), { range: a } = t(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();
}
};
}), ko = [
"http",
"https",
"ftp",
"ftps",
"mailto",
"tel",
"callto",
"sms",
"cid",
"xmpp"
], Co = "https", Tt = new P("node-selection-keyboard"), So = k(
() => ({
key: "nodeSelectionKeyboard",
prosemirrorPlugins: [
new T({
key: Tt,
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.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
const t = n.state.tr;
return n.dispatch(
t.insert(
n.state.tr.selection.$to.after(),
n.state.schema.nodes.paragraph.createChecked()
).setSelection(
new le(
t.doc.resolve(
n.state.tr.selection.$to.after() + 1
)
)
)
), !0;
}
}
return !1;
}
}
})
]
})
), Pt = new P("blocknote-placeholder"), xo = k(
({
editor: n,
options: e
}) => {
const t = e.placeholders;
return {
key: "placeholder",
prosemirrorPlugins: [
new T({
key: Pt,
view: (o) => {
const r = `placeholder-selector-${Qe()}`;
o.dom.classList.add(r);
const s = document.createElement("style"), i = n._tiptapEditor.options.injectNonce;
i && s.setAttribute("nonce", i), o.root instanceof window.ShadowRoot ? o.root.append(s) : o.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: m,
...h
} = t || {};
for (const [g, d] of Object.entries(h)) {
const p = `[data-content-type="${g}"]`;
a.insertRule(
`${c(p)} { content: ${JSON.stringify(
d
)}; }`
);
}
const f = "[data-is-only-empty-block]", u = "[data-is-empty-and-focused]";
a.insertRule(
`${c(f)} { content: ${JSON.stringify(
m
)}; }`
), a.insertRule(
`${c(u)} { 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: () => {
o.root instanceof window.ShadowRoot ? o.root.removeChild(s) : o.root.head.removeChild(s);
}
};
},
props: {
decorations: (o) => {
const { doc: r, selection: s } = o;
if (!n.isEditable || !s.empty || s.$from.parent.type.spec.code)
return;
const i = [];
o.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);
}
}
})
]
};
}
), oe = new P("previous-blocks"), Ot = {
// Numbered List Items
index: "index",
// Headings
level: "level",
// All Blocks
type: "type",
depth: "depth",
"depth-change": "depth-change"
}, Eo = k(() => {
let n;
return {
key: "previousBlockType",
prosemirrorPlugins: [
new T({
key: oe,
view(e) {
return {
update: async (t, o) => {
var r;
((r = this.key) == null ? void 0 : r.getState(t.state).updatedBlocks.size) > 0 && (n = setTimeout(() => {
t.dispatch(
t.state.tr.setMeta(oe, { 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, t, o, r) {
if (t.currentTransactionOldBlockAttrs = {}, t.updatedBlocks.clear(), !e.docChanged || o.doc.eq(r.doc))
return t;
const s = {}, i = X(
o.doc,
(l) => l.attrs.id
), a = new Map(
i.map((l) => [l.node.attrs.id, l])
), c = X(
r.doc,
(l) => l.attrs.id
);
for (const l of c) {
const m = a.get(l.node.attrs.id), h = m == null ? void 0 : m.node.firstChild, f = l.node.firstChild;
if (m && h && f) {
const u = {
index: f.attrs.index,
level: f.attrs.level,
type: f.type.name,
depth: r.doc.resolve(l.pos).depth
}, g = {
index: h.attrs.index,
level: h.attrs.level,
type: h.type.name,
depth: o.doc.resolve(m.pos).depth
};
s[l.node.attrs.id] = g, t.currentTransactionOldBlockAttrs[l.node.attrs.id] = g, JSON.stringify(g) !== JSON.stringify(u) && (g["depth-change"] = g.depth - u.depth, t.updatedBlocks.add(l.node.attrs.id));
}
}
return t.prevTransactionOldBlockAttrs = s, t;
}
},
props: {
decorations(e) {
const t = this.getState(e);
if (t.updatedBlocks.size === 0)
return;
const o = [];
return e.doc.descendants((r, s) => {
if (!r.attrs.id || !t.updatedBlocks.has(r.attrs.id))
return;
const i = t.currentTransactionOldBlockAttrs[r.attrs.id], a = {};
for (const [l, m] of Object.entries(i))
a["data-prev-" + Ot[l]] = m || "none";
const c = A.node(s, s + r.nodeSize, {
...a
});
o.push(c);
}), V.create(e.doc, o);
}
}
})
]
};
});
function Ce(n, e) {
var t, o;
for (; n && n.parentElement && n.parentElement !== e.dom && ((t = n.getAttribute) == null ? void 0 : t.call(n, "data-node-type")) !== "blockContainer"; )
n = n.parentElement;
if (((o = n.getAttribute) == null ? void 0 : o.call(n, "data-node-type")) === "blockContainer")
return { node: n, id: n.getAttribute("data-id") };
}
function Dt() {
const n = (e) => {
let t = e.children.length;
for (let o = 0; o < t; o++) {
const r = e.children[o];
if (r.type === "element" && (n(r), r.tagName === "u"))
if (r.children.length > 0) {
e.children.splice(o, 1, ...r.children);
const s = r.children.length - 1;
t += s, o += s;
} else
e.children.splice(o, 1), t--, o--;
}
};
return n;
}
function At() {
const n = (e) => {
var t;
if (e.children && "length" in e.children && e.children.length)
for (let o = e.children.length - 1; o >= 0; o--) {
const r = e.children[o], s = o + 1 < e.children.length ? e.children[o + 1] : void 0;
r.type === "element" && r.tagName === "input" && ((t = r.properties) == null ? void 0 : t.type) === "checkbox" && (s == null ? void 0 : s.type) === "element" && s.tagName === "p" ? (s.tagName = "span", s.children.splice(
0,
0,
at(document.createTextNode(" "))
)) : n(r);
}
};
return n;
}
function Mt() {
return (n) => {
lt(n, "element", (e, t, o) => {
var r, s, i, a;
if (o && 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"]) || "";
o.children[t] = {
type: "text",
value: ``
};
}
});
};
}
function Se(n) {
return it().use(ot, { fragment: !0 }).use(Mt).use(Dt).use(At).use(nt).use(rt).use(st, {
handlers: { text: (t) => t.value }
}).processSync(n).value;
}
function Io(n, e, t, o) {
const s = ve(e, t).exportBlocks(n, o);
return Se(s);
}
function Lt(n) {
const e = [];
return n.descendants((t) => {
var r, s;
const o = N(t);
return t.type.name === "blockContainer" && ((r = t.firstChild) == null ? void 0 : r.type.name) === "blockGroup" ? !0 : t.type.name === "columnList" && t.childCount === 1 ? ((s = t.firstChild) == null || s.forEach((i) => {
e.push(R(i, o));
}), !1) : t.type.isInGroup("bnBlock") ? (e.push(R(t, o)), !1) : !0;
}), e;
}
class B extends L {
constructor(t, o) {
super(t, o);
b(this, "nodes");
const r = t.node();
this.nodes = [], t.doc.nodesBetween(t.pos, o.pos, (s, i, a) => {
if (a !== null && a.eq(r))
return this.nodes.push(s), !1;
});
}
static create(t, o, r = o) {
return new B(t.resolve(o), t.resolve(r));
}
content() {
return new tt(ge.from(this.nodes), 0, 0);
}
eq(t) {
if (!(t instanceof B) || this.nodes.length !== t.nodes.length || this.from !== t.from || this.to !== t.to)
return !1;
for (let o = 0; o < this.nodes.length; o++)
if (!this.nodes[o].eq(t.nodes[o]))
return !1;
return !0;
}
map(t, o) {
const r = o.mapResult(this.from), s = o.mapResult(this.to);
return s.deleted ? L.near(t.resolve(r.pos)) : r.deleted ? L.near(t.resolve(s.pos)) : new B(
t.resolve(r.pos),
t.resolve(s.pos)
);
}
toJSON() {
return { type: "multiple-node", anchor: this.anchor, head: this.head };
}
}
L.jsonID("multiple-node", B);
let x;
function Nt(n, e) {
let t, o;
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);
t = e.resolve(a - 1).pos, o = e.resolve(c + 1).pos;
} else
t = n.from, o = n.to;
return { from: t, to: o };
}
function ne(n, e, t = e) {
e === t && (t += n.state.doc.resolve(e + 1).node().nodeSize);
const o = n.domAtPos(e).node.cloneNode(!0), r = n.domAtPos(e).node, s = (h, f) => Array.prototype.indexOf.call(h.children, f), 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(t - 1).node.parentElement
);
for (let h = r.childElementCount - 1; h >= 0; h--)
(h > a || h < i) && o.removeChild(o.children[h]);
xe(n.root), x = o;
const c = x.getElementsByTagName("iframe");
for (let h = 0; h < c.length; h++) {
const f = c[h], u = f.parentElement;
u && u.removeChild(f);
}
const m = n.dom.className.split(" ").filter(
(h) => h !== "ProseMirror" && h !== "bn-root" && h !== "bn-editor"
).join(" ");
x.className = x.className + " bn-drag-preview " + m, n.root instanceof ShadowRoot ? n.root.appendChild(x) : n.root.body.appendChild(x);
}
function xe(n) {
x !== void 0 && (n instanceof ShadowRoot ? n.removeChild(x) : n.body.removeChild(x), x = void 0);
}
function Rt(n, e, t) {
if (!n.dataTransfer || t.headless)
return;
const o = t.prosemirrorView, r = he(e.id, o.state.doc);
if (!r)
throw new Error(`Block with ID ${e.id} not found`);
const s = r.posBeforeNode;
if (s != null) {
const i = o.state.selection, a = o.state.doc, { from: c, to: l } = Nt(i, a), m = c <= s && s < l, h = i.$anchor.node() !== i.$head.node() || i instanceof B;
m && h ? (o.dispatch(
o.state.tr.setSelection(B.create(a, c, l))
), ne(o, c, l)) : (o.dispatch(
o.state.tr.setSelection(ae.create(o.state.doc, s))
), ne(o, s));
const f = o.state.selection.content(), u = t.pmSchema, g = o.serializeForClipboard(f).dom.innerHTML, d = ve(u, t), p = Lt(f.content), w = d.exportBlocks(p, {}), y = Se(w);
n.dataTransfer.clearData(), n.dataTransfer.setData("blocknote/html", g), n.dataTransfer.setData("text/html", w), n.dataTransfer.setData("text/plain", y), n.dataTransfer.effectAllowed = "move", n.dataTransfer.setDragImage(x, 0, 0);
}
}
const re = 250;
function U(n, e, t = !0) {
const o = n.root.elementsFromPoint(e.left, e.top);
for (const r of o)
if (n.dom.contains(r))
return t && 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
) : Ce(r, n);
}
function Vt(n, e) {
if (!e.dom.firstChild)
return;
const t = e.dom.firstChild.getBoundingClientRect(), o = {
// Clamps the x position to the editor's bounding box.
left: Math.min(
Math.max(t.left + 10, n.x),
t.right - 10
),
top: n.y
}, r = U(e, o);
if (!r)
return;
const s = r.node.getBoundingClientRect();
return U(
e,
{
left: s.right - 10,
top: n.y
},
!1
);
}
class Ht {
constructor(e, t, o) {
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 o, 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 > re) {
(o = this.state) != null && o.show && (this.state.show = !1, this.updateState(this.state));
return;
}
const t = Vt(this.mousePos, this.pmView);
if (!t || !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")) === t.id) && (this.hoveredBlock = t.node, this.editor.isEditable)) {
const c = t.node.getBoundingClientRect(), l = t.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 t = (i = e.dataTransfer) == null ? void 0 : i.getData("blocknote/html");
if (!t || this.pmView.dragging)
return;
const o = document.createElement("div");
o.innerHTML = t;
const s = Ze.fromSchema(this.pmView.state.schema).parse(o, {
topNode: this.pmView.state.schema.nodes.blockGroup.create()
});
this.pmView.dragging = {
slice: new et(s.content, 0, 0),
move: !0
};
});
/**
* Finds the closest editor visually to the given coordinates
*/
b(this, "findClosestEditorElement", (e) => {
const t = Array.from(this.pmView.root.querySelectorAll(".bn-editor"));
if (t.length === 0)
return null;
let o = t[0], r = Number.MAX_VALUE;
return t.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, o = s);
}), {
element: o,
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) => {
if (e.synthetic)
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 c;
const t = !((c = e.dataTransfer) != null && c.types.includes("blocknote/html")) && !!this.pmView.dragging, o = !!this.isDragOrigin, r = t || o, s = this.findClosestEditorElement(e);
if (!s || s.distance > re)
return;
const i = s.element === this.pmView.dom, a = i && s.distance === 0;
if (!(!i && !r))
return {
isDropPoint: i,
isDropWithinEditorBounds: a,
isDragOrigin: r
};
});
/**
* 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) => {
if (e.synthetic)
return;
const t = this.getDragEventContext(e);
if (!t) {
this.closeDropCursor();
return;
}
const { isDropPoint: o, isDropWithinEditorBounds: r, isDragOrigin: s } = t;
if (!r && o && this.dispatchSyntheticEvent(e), o) {
if (this.pmView.dragging)
return;
this.pmView.dispatch(
this.pmView.state.tr.setSelection(
je.create(
this.pmView.state.tr.doc,
this.pmView.state.tr.selection.anchor
)
)
);
return;
} else if (s) {
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 t;
(t = this.state) != null && t.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 t = this.pmView.dom.getBoundingClientRect(), o = this.mousePos.x > t.left && this.mousePos.x < t.right && this.mousePos.y > t.top && this.mousePos.y < t.bottom, r = this.pmView.dom.parentElement;
if (
// Cursor is within the editor area
o && // 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 = t, this.emitUpdate = () => {
if (!this.state)
throw new Error("Attempting to update uninitialized side menu");
o(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 t = new Event(e.type, e), o = this.pmView.dom.firstChild.getBoundingClientRect();
t.clientX = e.clientX, t.clientY = e.clientY, t.clientX = Math.min(
Math.max(e.clientX, o.left),
o.left + o.width
), t.clientY = Math.min(
Math.max(e.clientY, o.top),
o.top + o.height
), t.dataTransfer = e.dataTransfer, t.preventDefault = () => e.preventDefault(), t.synthetic = !0, this.pmView.dom.dispatchEvent(t);
}
// 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 the user to click the button again without moving the cursor. This
// would otherwise not update the side menu, and so clicking the button again
// would attempt to remove the same block again, causing an error.
update(e, t) {
var r;
!t.doc.eq(this.pmView.state.doc) && ((r = this.state) != null && r.show) && this.updateStateFromMousePos();
}
destroy() {
var e;
(e = this.state) != null && e.show && (this.state.show = !1, this.emitUpdate(this.state)), this.pmView.root.removeEventListener(
"mousemove",
this.onMouseMove,
!0
), this.pmView.root.removeEventListener(
"dragstart",
this.onDragStart
), this.pmView.root.removeEventListener(
"dragover",
this.onDragOver
), this.pmView.root.removeEventListener(
"drop",
this.onDrop,
!0
), this.pmView.root.removeEventListener(
"dragend",
this.onDragEnd,
!0
), this.pmView.root.removeEventListener(
"keydown"