UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

1,616 lines (1,615 loc) 94.9 kB
var Oe = Object.defineProperty; var _e = (e, t, n) => t in e ? Oe(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; var M = (e, t, n) => _e(e, typeof t != "symbol" ? t + "" : t, n); import { TableMap as be, goToNextCell as ne, columnResizing as Ve, tableEditing as Re, CellSelection as Ce, TableView as We } from "prosemirror-tables"; import { Node as N, Mark as Fe, Extension as $e, callOrReturn as Ue, getExtensionField as qe, mergeAttributes as z, findParentNode as je } from "@tiptap/core"; import { TextSelection as ke, Plugin as Ge, PluginKey as Ze } from "prosemirror-state"; import { DecorationSet as oe, Decoration as re } from "prosemirror-view"; import { ReplaceStep as ze, Mapping as Xe } from "prosemirror-transform"; import { ySyncPluginKey as ae, absolutePositionToRelativePosition as Ke, relativePositionToAbsolutePosition as Qe } from "y-prosemirror"; import { c as k, a as ye } from "./BlockNoteExtension-C2X7LW-V.js"; import { b as X, v as Je, g as O, a as ve, i as se, t as Ye, U as et, q as tt, A as G, r as nt, w as Ee, J as ot } from "./blockToNode-BNoNIXU7.js"; import rt from "@tiptap/extension-bold"; import at from "@tiptap/extension-code"; import st from "@tiptap/extension-italic"; import it from "@tiptap/extension-strike"; import ct from "@tiptap/extension-underline"; import { DOMParser as xe, Fragment as lt, DOMSerializer as ie } from "@tiptap/pm/model"; import { createHighlightPlugin as dt } from "prosemirror-highlight"; import { createParser as ut } from "prosemirror-highlight/shiki"; import { Slice as we, Fragment as F, DOMParser as Se } from "prosemirror-model"; import { Plugin as pt, PluginKey as ft } from "@tiptap/pm/state"; import { DecorationSet as ce, Decoration as ht } from "@tiptap/pm/view"; const mt = () => typeof navigator < "u" && (/Mac/.test(navigator.platform) || /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)); function L(e, t = "Ctrl") { return mt() ? e.replace("Mod", "⌘") : e.replace("Mod", t); } function D(...e) { return [ // Converts to & from set to remove duplicates. ...new Set( e.filter((t) => t).join(" ").split(" ") ) ].join(" "); } const uo = () => /^((?!chrome|android).)*safari/i.test(navigator.userAgent); function gt(e, t, n, o) { const r = document.createElement("div"); r.className = D( "bn-block-content", n.class ), r.setAttribute("data-content-type", e); for (const [s, i] of Object.entries(n)) s !== "class" && r.setAttribute(s, i); const a = document.createElement(t); a.className = D( "bn-inline-content", o.class ); for (const [s, i] of Object.entries( o )) s !== "class" && a.setAttribute(s, i); return r.appendChild(a), { dom: r, contentDOM: a }; } const le = (e, t) => { let n = X(e, t.pmSchema); n.type.name === "blockContainer" && (n = n.firstChild); const o = t.pmSchema.nodes[n.type.name].spec.toDOM; if (o === void 0) throw new Error( "This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`." ); const r = o(n); if (typeof r != "object" || !("dom" in r)) throw new Error( "Cannot use this block's default HTML serialization as its corresponding TipTap node's `renderHTML` function does not return an object with the `dom` property." ); return r; }; function bt(e, t = "<br>") { const n = e.querySelectorAll("p"); if (n.length > 1) { const o = n[0]; for (let r = 1; r < n.length; r++) { const a = n[r]; o.innerHTML += t + a.innerHTML, a.remove(); } } } function W(e) { return "data-" + e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); } function po(e) { const t = e.split("/"); return !t.length || // invalid? t[t.length - 1] === "" ? e : t[t.length - 1]; } function fo(e) { var n; const t = [ "mp4", "webm", "ogg", "mov", "mkv", "flv", "avi", "wmv", "m4v" ]; try { const r = ((n = new URL(e).pathname.split(".").pop()) == null ? void 0 : n.toLowerCase()) || ""; return t.includes(r); } catch { return !1; } } function Ct(e) { const t = {}; return Object.entries(e).forEach(([n, o]) => { t[n] = { default: o.default, keepOnSplit: !0, // Props are displayed in kebab-case as HTML attributes. If a prop's // value is the same as its default, we don't display an HTML // attribute for it. parseHTML: (r) => { const a = r.getAttribute(W(n)); if (a === null) return null; if (o.default === void 0 && o.type === "boolean" || o.default !== void 0 && typeof o.default == "boolean") return a === "true" ? !0 : a === "false" ? !1 : null; if (o.default === void 0 && o.type === "number" || o.default !== void 0 && typeof o.default == "number") { const s = parseFloat(a); return !Number.isNaN(s) && Number.isFinite(s) ? s : null; } return a; }, renderHTML: (r) => r[n] !== o.default ? { [W(n)]: r[n] } : {} }; }), t; } function kt(e, t, n, o) { const r = e(); if (r === void 0) throw new Error("Cannot find node position"); const s = n.state.doc.resolve(r).node().attrs.id; if (!s) throw new Error("Block doesn't have id"); const i = t.getBlock(s); if (i.type !== o) throw new Error("Block type does not match"); return i; } function Z(e, t, n, o, r = !1, a) { const s = document.createElement("div"); if (a !== void 0) for (const [i, c] of Object.entries(a)) i !== "class" && s.setAttribute(i, c); s.className = D( "bn-block-content", (a == null ? void 0 : a.class) || "" ), s.setAttribute("data-content-type", t); for (const [i, c] of Object.entries(n)) { const u = o[i].default; c !== u && s.setAttribute(W(i), c); } return r && s.setAttribute("data-file-block", ""), s.appendChild(e.dom), e.contentDOM && (e.contentDOM.className = D( "bn-inline-content", e.contentDOM.className )), { ...e, dom: s }; } function yt(e, t, n) { return { config: { type: e.type, content: e.content, propSchema: t }, implementation: { node: e.node, render: le, toExternalHTML: le }, extensions: n }; } function vt(e, t) { e.stopEvent = (n) => (n.type === "mousedown" && setTimeout(() => { t.view.dom.blur(); }, 10), !0); } function Et(e, t) { const n = [ { tag: "[data-content-type=" + e.type + "]", contentElement: ".bn-inline-content" } ]; return t.parse && n.push({ tag: "*", getAttrs(o) { var a; if (typeof o == "string") return !1; const r = (a = t.parse) == null ? void 0 : a.call(t, o); return r === void 0 ? !1 : r; }, // Because we do the parsing ourselves, we want to preserve whitespace for content we've parsed preserveWhitespace: !0, getContent: e.content === "inline" || e.content === "none" ? (o, r) => { var a; if (t.parseContent) return t.parseContent({ el: o, schema: r }); if (e.content === "inline") { const i = o.cloneNode(!0); return bt( i, (a = t.meta) != null && a.code ? ` ` : "<br>" ), xe.fromSchema(r).parse(i, { topNode: r.nodes.paragraph.create(), preserveWhitespace: !0 }).content; } return lt.empty; } : void 0 }), n; } function ho(e, t, n, o) { var a, s, i, c; const r = t.node || N.create({ name: e.type, content: e.content === "inline" ? "inline*" : e.content === "none" ? "" : e.content, group: "blockContent", selectable: ((a = t.meta) == null ? void 0 : a.selectable) ?? !0, isolating: ((s = t.meta) == null ? void 0 : s.isolating) ?? !0, code: ((i = t.meta) == null ? void 0 : i.code) ?? !1, defining: ((c = t.meta) == null ? void 0 : c.defining) ?? !0, priority: o, addAttributes() { return Ct(e.propSchema); }, parseHTML() { return Et(e, t); }, renderHTML({ HTMLAttributes: l }) { var d; const u = document.createElement("div"); return Z( { dom: u, contentDOM: e.content === "inline" ? u : void 0 }, e.type, {}, e.propSchema, ((d = t.meta) == null ? void 0 : d.fileBlockAccept) !== void 0, l ); }, addNodeView() { return (l) => { var f, E; const u = this.options.editor, d = kt( l.getPos, u, this.editor, e.type ), p = ((f = this.options.domAttributes) == null ? void 0 : f.blockContent) || {}, m = t.render.call( { blockContentDOMAttributes: p, props: l, renderType: "nodeView" }, d, u ); return ((E = t.meta) == null ? void 0 : E.selectable) === !1 && vt(m, this.editor), m; }; } }); if (r.name !== e.type) throw new Error( "Node name does not match block type. This is a bug in BlockNote." ); return { config: e, implementation: { ...t, node: r, render(l, u) { var p; const d = ((p = r.options.domAttributes) == null ? void 0 : p.blockContent) || {}; return t.render.call( { blockContentDOMAttributes: d, props: void 0, renderType: "dom" }, l, u ); }, // TODO: this should not have wrapInBlockStructure and generally be a lot simpler // post-processing in externalHTMLExporter should not be necessary toExternalHTML: (l, u) => { var p, m; const d = ((p = r.options.domAttributes) == null ? void 0 : p.blockContent) || {}; return ((m = t.toExternalHTML) == null ? void 0 : m.call( { blockContentDOMAttributes: d }, l, u )) ?? t.render.call( { blockContentDOMAttributes: d, renderType: "dom", props: void 0 }, l, u ); } }, extensions: n }; } function mo(e) { return e; } function v(e, t, n) { return (o = {}) => { const r = typeof e == "function" ? e(o) : e, a = typeof t == "function" ? t(o) : t, s = n ? typeof n == "function" ? n(o) : n : void 0; return { config: r, implementation: { ...a, // TODO: this should not have wrapInBlockStructure and generally be a lot simpler // post-processing in externalHTMLExporter should not be necessary toExternalHTML(i, c) { var u, d; const l = (u = a.toExternalHTML) == null ? void 0 : u.call( { blockContentDOMAttributes: this.blockContentDOMAttributes }, i, c ); if (l !== void 0) return Z( l, i.type, i.props, r.propSchema, ((d = a.meta) == null ? void 0 : d.fileBlockAccept) !== void 0 ); }, render(i, c) { var d; const l = a.render.call( { blockContentDOMAttributes: this.blockContentDOMAttributes, renderType: this.renderType, props: this.props }, i, c ); return Z( l, i.type, i.props, r.propSchema, ((d = a.meta) == null ? void 0 : d.fileBlockAccept) !== void 0, this.blockContentDOMAttributes ); } }, extensions: s }; }; } function go(e, t, n, o) { return e.dom.setAttribute("data-inline-content-type", t), Object.entries(n).filter(([r, a]) => { const s = o[r]; return a !== s.default; }).map(([r, a]) => [W(r), a]).forEach(([r, a]) => e.dom.setAttribute(r, a)), e.contentDOM && e.contentDOM.setAttribute("data-editable", ""), e; } function bo(e) { return { Backspace: ({ editor: t }) => { const n = t.state.selection.$from; return t.state.selection.empty && n.node().type.name === e.type && n.parentOffset === 0; } }; } function xt(e, t) { return { config: e, implementation: t }; } function Co(e, t, n) { return xt( { type: e.name, propSchema: t, content: e.config.content === "inline*" ? "styled" : "none" }, { ...n, node: e } ); } function wt(e) { return Object.fromEntries( Object.entries(e).map(([t, n]) => [t, n.config]) ); } function St(e) { return e === "boolean" ? {} : { stringValue: { default: void 0, keepOnSplit: !0, parseHTML: (t) => t.getAttribute("data-value"), renderHTML: (t) => t.stringValue !== void 0 ? { "data-value": t.stringValue } : {} } }; } function V(e, t, n, o) { return e.dom.setAttribute("data-style-type", t), o === "string" && e.dom.setAttribute("data-value", n), e.contentDOM && e.contentDOM.setAttribute("data-editable", ""), e; } function Me(e, t) { return { config: e, implementation: t }; } function H(e, t) { return Me( { type: e.name, propSchema: t }, { mark: e, render(n, o) { const r = o.pmSchema.marks[e.name].spec.toDOM; if (r === void 0) throw new Error( "This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`." ); const a = o.pmSchema.mark(e.name, { stringValue: n }), s = ie.renderSpec( document, r(a, !0) ); if (typeof s != "object" || !("dom" in s)) throw new Error( "Cannot use this block's default HTML serialization as its corresponding TipTap mark's `renderHTML` function does not return an object with the `dom` property." ); return s; }, toExternalHTML(n, o) { const r = o.pmSchema.marks[e.name].spec.toDOM; if (r === void 0) throw new Error( "This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`." ); const a = o.pmSchema.mark(e.name, { stringValue: n }), s = ie.renderSpec( document, r(a, !0) ); if (typeof s != "object" || !("dom" in s)) throw new Error( "Cannot use this block's default HTML serialization as its corresponding TipTap mark's `renderHTML` function does not return an object with the `dom` property." ); return s; } } ); } function Mt(e) { return Object.fromEntries( Object.entries(e).map(([t, n]) => [t, n.config]) ); } function Lt(e, t) { const n = [ { tag: `[data-style-type="${e.type}"]`, contentElement: (o) => { const r = o; return r.matches("[data-editable]") ? r : r.querySelector("[data-editable]") || r; } } ]; return t && n.push({ tag: "*", // By default, styles can overlap each other, so the rules should not // completely consume the element they parse (which can have multiple // styles). consuming: !1, getAttrs(o) { if (typeof o == "string") return !1; const r = t == null ? void 0 : t(o); return r === void 0 ? !1 : { stringValue: r }; } }), n; } function Le(e, t) { const n = Fe.create({ name: e.type, addAttributes() { return St(e.propSchema); }, parseHTML() { return Lt(e, t.parse); }, renderHTML({ mark: o }) { const r = (t.toExternalHTML || t.render)(o.attrs.stringValue); return V( r, e.type, o.attrs.stringValue, e.propSchema ); }, addMarkView() { return ({ mark: o }) => { const r = t.render(o.attrs.stringValue); return V( r, e.type, o.attrs.stringValue, e.propSchema ); }; } }); return Me(e, { ...t, mark: n, render: (o) => { const r = t.render(o); return V( r, e.type, o, e.propSchema ); }, toExternalHTML: (o) => { const r = (t.toExternalHTML || t.render)(o); return V( r, e.type, o, e.propSchema ); } }); } function Tt(e, t) { let n, o; if (t.firstChild.descendants((r, a) => n ? !1 : !Bt(r) || r.attrs.id !== e ? !0 : (n = r, o = a + 1, !1)), !(n === void 0 || o === void 0)) return { node: n, posBeforeNode: o }; } function Bt(e) { return e.type.isInGroup("bnBlock"); } const ko = (e, t) => ({ tr: n, dispatch: o }) => (o && K(n, e, t), !0); function K(e, t, n, o, r) { const a = Je(e.doc.resolve(t)); let s = null; a.blockNoteType === "table" && (s = Pt(e)); const i = O(e); if (o !== void 0 && r !== void 0 && o > r) throw new Error("Invalid replaceFromPos or replaceToPos"); const c = i.nodes[a.blockNoteType], l = i.nodes[n.type || a.blockNoteType], u = l.isInGroup("bnBlock") ? l : i.nodes.blockContainer; if (a.isBlockContainer && l.isInGroup("blockContent")) { const d = o !== void 0 && o > a.blockContent.beforePos && o < a.blockContent.afterPos ? o - a.blockContent.beforePos - 1 : void 0, p = r !== void 0 && r > a.blockContent.beforePos && r < a.blockContent.afterPos ? r - a.blockContent.beforePos - 1 : void 0; de(n, e, a), At( n, e, c, l, a, d, p ); } else if (!a.isBlockContainer && l.isInGroup("bnBlock")) de(n, e, a); else { const d = ve(a.bnBlock.node, i); e.replaceWith( a.bnBlock.beforePos, a.bnBlock.afterPos, X( { children: d.children, // if no children are passed in, use existing children ...n }, i ) ); return; } e.setNodeMarkup(a.bnBlock.beforePos, u, { ...a.bnBlock.node.attrs, ...n.props }), s && Nt(e, a, s); } function At(e, t, n, o, r, a, s) { const i = O(t); let c = "keep"; if (e.content) if (typeof e.content == "string") c = se( [e.content], i, o.name ); else if (Array.isArray(e.content)) c = se(e.content, i, o.name); else if (e.content.type === "tableContent") c = Ye(e.content, i); else throw new et(e.content.type); else n.spec.content === "" || o.spec.content !== n.spec.content && (c = []); if (c === "keep") t.setNodeMarkup(r.blockContent.beforePos, o, { ...r.blockContent.node.attrs, ...e.props }); else if (a !== void 0 || s !== void 0) { t.setNodeMarkup(r.blockContent.beforePos, o, { ...r.blockContent.node.attrs, ...e.props }); const l = r.blockContent.beforePos + 1 + (a ?? 0), u = r.blockContent.beforePos + 1 + (s ?? r.blockContent.node.content.size), d = t.doc.resolve(r.blockContent.beforePos).depth, p = t.doc.resolve(l).depth, m = t.doc.resolve(u).depth; t.replace( l, u, new we( F.from(c), p - d - 1, m - d - 1 ) ); } else t.replaceWith( r.blockContent.beforePos, r.blockContent.afterPos, o.createChecked( { ...r.blockContent.node.attrs, ...e.props }, c ) ); } function de(e, t, n) { const o = O(t); if (e.children !== void 0 && e.children.length > 0) { const r = e.children.map((a) => X(a, o)); if (n.childContainer) t.step( new ze( n.childContainer.beforePos + 1, n.childContainer.afterPos - 1, new we(F.from(r), 0, 0) ) ); else { if (!n.isBlockContainer) throw new Error("impossible"); t.insert( n.blockContent.afterPos, o.nodes.blockGroup.createChecked({}, r) ); } } } function yo(e, t, n, o, r) { const a = typeof t == "string" ? t : t.id, s = Tt(a, e.doc); if (!s) throw new Error(`Block with ID ${a} not found`); K( e, s.posBeforeNode, n, o, r ); const i = e.doc.resolve(s.posBeforeNode + 1).node(), c = O(e); return ve(i, c); } function Pt(e) { const t = "selection" in e ? e.selection : null; if (!(t instanceof ke)) return null; const n = e.doc.resolve(t.head); let o = -1, r = -1; for (let w = n.depth; w >= 0; w--) { const x = n.node(w).type.name; if (o < 0 && (x === "tableCell" || x === "tableHeader") && (o = w), x === "table") { r = w; break; } } if (o < 0 || r < 0) return null; const a = n.before(o), s = n.before(r), i = e.doc.nodeAt(s); if (!i || i.type.name !== "table") return null; const c = be.get(i), l = a - (s + 1), u = c.map.indexOf(l); if (u < 0) return null; const d = Math.floor(u / c.width), p = u % c.width, f = a + 1 + 1, E = Math.max(0, t.head - f); return { row: d, col: p, offset: E }; } function Nt(e, t, n) { var w; if (t.blockNoteType !== "table") return !1; let o = -1; if (t.isBlockContainer) o = e.mapping.map(t.blockContent.beforePos); else { const x = e.mapping.map(t.bnBlock.beforePos), P = x + (((w = e.doc.nodeAt(x)) == null ? void 0 : w.nodeSize) || 0); e.doc.nodesBetween(x, P, (g, S) => g.type.name === "table" ? (o = S, !1) : !0); } const r = o >= 0 ? e.doc.nodeAt(o) : null; if (!r || r.type.name !== "table") return !1; const a = be.get(r), s = Math.max(0, Math.min(n.row, a.height - 1)), i = Math.max(0, Math.min(n.col, a.width - 1)), c = s * a.width + i, l = a.map[c]; if (l == null) return !1; const d = o + 1 + l + 1, p = e.doc.nodeAt(d), m = d + 1, f = p ? p.content.size : 0, E = m + Math.max(0, Math.min(n.offset, f)); return "selection" in e && e.setSelection(ke.create(e.doc, E)), !0; } const T = { gray: { text: "#9b9a97", background: "#ebeced" }, brown: { text: "#64473a", background: "#e9e5e3" }, red: { text: "#e03e3e", background: "#fbe4e4" }, orange: { text: "#d9730d", background: "#f6e9d9" }, yellow: { text: "#dfab01", background: "#fbf3db" }, green: { text: "#4d6461", background: "#ddedea" }, blue: { text: "#0b6e99", background: "#ddebf1" }, purple: { text: "#6940a5", background: "#eae4f2" }, pink: { text: "#ad1a72", background: "#f4dfeb" } }, vo = { gray: { text: "#bebdb8", background: "#9b9a97" }, brown: { text: "#8e6552", background: "#64473a" }, red: { text: "#ec4040", background: "#be3434" }, orange: { text: "#e3790d", background: "#b7600a" }, yellow: { text: "#dfab01", background: "#b58b00" }, green: { text: "#6b8b87", background: "#4d6461" }, blue: { text: "#0e87bc", background: "#0b6e99" }, purple: { text: "#8552d7", background: "#6940a5" }, pink: { text: "#da208f", background: "#ad1a72" } }, h = { backgroundColor: { default: "default" }, textColor: { default: "default" }, textAlignment: { default: "left", values: ["left", "center", "right", "justify"] } }, y = (e) => { const t = {}; return e.hasAttribute("data-background-color") ? t.backgroundColor = e.getAttribute("data-background-color") : e.style.backgroundColor && (t.backgroundColor = e.style.backgroundColor), e.hasAttribute("data-text-color") ? t.textColor = e.getAttribute("data-text-color") : e.style.color && (t.textColor = e.style.color), t.textAlignment = h.textAlignment.values.includes( e.style.textAlign ) ? e.style.textAlign : void 0, t; }, A = (e, t) => { e.backgroundColor && e.backgroundColor !== h.backgroundColor.default && (t.style.backgroundColor = e.backgroundColor in T ? T[e.backgroundColor].background : e.backgroundColor), e.textColor && e.textColor !== h.textColor.default && (t.style.color = e.textColor in T ? T[e.textColor].text : e.textColor), e.textAlignment && e.textAlignment !== h.textAlignment.default && (t.style.textAlign = e.textAlignment); }, Eo = (e = "backgroundColor") => ({ default: h.backgroundColor.default, parseHTML: (t) => t.hasAttribute("data-background-color") ? t.getAttribute("data-background-color") : t.style.backgroundColor ? t.style.backgroundColor : h.backgroundColor.default, renderHTML: (t) => t[e] === h.backgroundColor.default ? {} : { "data-background-color": t[e] } }), xo = (e = "textColor") => ({ default: h.textColor.default, parseHTML: (t) => t.hasAttribute("data-text-color") ? t.getAttribute("data-text-color") : t.style.color ? t.style.color : h.textColor.default, renderHTML: (t) => t[e] === h.textColor.default ? {} : { "data-text-color": t[e] } }), wo = (e = "textAlignment") => ({ default: h.textAlignment.default, parseHTML: (t) => t.hasAttribute("data-text-alignment") ? t.getAttribute("data-text-alignment") : t.style.textAlign ? t.style.textAlign : h.textAlignment.default, renderHTML: (t) => t[e] === h.textAlignment.default ? {} : { "data-text-alignment": t[e] } }), $ = (e, t) => { const n = e.querySelector( t ); if (!n) return; const o = e.querySelector("figcaption"), r = (o == null ? void 0 : o.textContent) ?? void 0; return { targetElement: n, caption: r }; }, I = k(({ editor: e }) => { const t = ye(void 0); function n() { t.setState(void 0); } return { key: "filePanel", store: t, mount({ signal: o }) { const r = e.onChange( n, // Don't trigger if the changes are caused by a remote user. !1 ), a = e.onSelectionChange( n, // Don't trigger if the changes are caused by a remote user. !1 ); o.addEventListener("abort", () => { r(), a(); }); }, closeMenu: n, showMenu(o) { t.setState(o); } }; }), Ht = (e, t, n) => { const o = document.createElement("div"); o.className = "bn-add-file-button"; const r = document.createElement("div"); r.className = "bn-add-file-button-icon", n ? r.appendChild(n) : r.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg>', o.appendChild(r); const a = document.createElement("p"); a.className = "bn-add-file-button-text", a.innerHTML = e.type in t.dictionary.file_blocks.add_button_text ? t.dictionary.file_blocks.add_button_text[e.type] : t.dictionary.file_blocks.add_button_text.file, o.appendChild(a); const s = (c) => { c.preventDefault(), c.stopPropagation(); }, i = () => { var c; t.isEditable && ((c = t.getExtension(I)) == null || c.showMenu(e.id)); }; return o.addEventListener( "mousedown", s, !0 ), o.addEventListener("click", i, !0), { dom: o, destroy: () => { o.removeEventListener( "mousedown", s, !0 ), o.removeEventListener( "click", i, !0 ); } }; }, It = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg>', Dt = (e) => { const t = document.createElement("div"); t.className = "bn-file-name-with-icon"; const n = document.createElement("div"); n.className = "bn-file-icon", n.innerHTML = It, t.appendChild(n); const o = document.createElement("p"); return o.className = "bn-file-name", o.textContent = e.props.name, t.appendChild(o), { dom: t }; }, Q = (e, t, n, o) => { const r = document.createElement("div"); if (r.className = "bn-file-block-content-wrapper", e.props.url === "") { const s = Ht(e, t, o); r.appendChild(s.dom); const i = t.onUploadStart((c) => { if (c === e.id) { r.removeChild(s.dom); const l = document.createElement("div"); l.className = "bn-file-loading-preview", l.textContent = "Loading...", r.appendChild(l); } }); return { dom: r, destroy: () => { i(), s.destroy(); } }; } const a = { dom: r }; if (e.props.showPreview === !1 || !n) { const s = Dt(e); r.appendChild(s.dom), a.destroy = () => { var i; (i = s.destroy) == null || i.call(s); }; } else r.appendChild(n.dom); if (e.props.caption) { const s = document.createElement("p"); s.className = "bn-file-caption", s.textContent = e.props.caption, r.appendChild(s); } return a; }, J = (e, t) => { const n = document.createElement("figure"), o = document.createElement("figcaption"); return o.textContent = t, n.appendChild(e), n.appendChild(o), { dom: n }; }, U = (e, t) => { const n = document.createElement("div"), o = document.createElement("p"); return o.textContent = t, n.appendChild(e), n.appendChild(o), { dom: n }; }, ue = (e) => ({ url: e.src || void 0 }), Ot = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 16.0001H5.88889L11.1834 20.3319C11.2727 20.405 11.3846 20.4449 11.5 20.4449C11.7761 20.4449 12 20.2211 12 19.9449V4.05519C12 3.93977 11.9601 3.8279 11.887 3.73857C11.7121 3.52485 11.3971 3.49335 11.1834 3.66821L5.88889 8.00007H2C1.44772 8.00007 1 8.44778 1 9.00007V15.0001C1 15.5524 1.44772 16.0001 2 16.0001ZM23 12C23 15.292 21.5539 18.2463 19.2622 20.2622L17.8445 18.8444C19.7758 17.1937 21 14.7398 21 12C21 9.26016 19.7758 6.80629 17.8445 5.15557L19.2622 3.73779C21.5539 5.75368 23 8.70795 23 12ZM18 12C18 10.0883 17.106 8.38548 15.7133 7.28673L14.2842 8.71584C15.3213 9.43855 16 10.64 16 12C16 13.36 15.3213 14.5614 14.2842 15.2841L15.7133 16.7132C17.106 15.6145 18 13.9116 18 12Z"></path></svg>', _t = (e) => ({ type: "audio", propSchema: { backgroundColor: h.backgroundColor, // File name. name: { default: "" }, // File url. url: { default: "" }, // File caption. caption: { default: "" }, showPreview: { default: !0 } }, content: "none" }), Vt = (e = {}) => (t) => { if (t.tagName === "AUDIO") { if (t.closest("figure")) return; const { backgroundColor: n } = y(t); return { ...ue(t), backgroundColor: n }; } if (t.tagName === "FIGURE") { const n = $(t, "audio"); if (!n) return; const { targetElement: o, caption: r } = n, { backgroundColor: a } = y(t); return { ...ue(o), backgroundColor: a, caption: r }; } }, Rt = (e = {}) => (t, n) => { const o = document.createElement("div"); o.innerHTML = e.icon ?? Ot; const r = document.createElement("audio"); return r.className = "bn-audio", n.resolveFileUrl ? n.resolveFileUrl(t.props.url).then((a) => { r.src = a; }) : r.src = t.props.url, r.controls = !0, r.contentEditable = "false", r.draggable = !1, Q( t, n, { dom: r }, o.firstElementChild ); }, Wt = (e = {}) => (t, n) => { if (!t.props.url) { const r = document.createElement("p"); return r.textContent = "Add audio", { dom: r }; } let o; return t.props.showPreview ? (o = document.createElement("audio"), o.src = t.props.url) : (o = document.createElement("a"), o.href = t.props.url, o.textContent = t.props.name || t.props.url), t.props.caption ? t.props.showPreview ? J(o, t.props.caption) : U(o, t.props.caption) : { dom: o }; }, Ft = v( _t, (e) => ({ meta: { fileBlockAccept: ["audio/*"] }, parse: Vt(e), render: Rt(e), toExternalHTML: Wt(e), runsBefore: ["file"] }) ), pe = Symbol.for("blocknote.shikiParser"), j = Symbol.for( "blocknote.shikiHighlighterPromise" ); function $t(e) { const t = globalThis; let n, o, r = !1; return dt({ parser: (s) => { if (!e.createHighlighter) return process.env.NODE_ENV === "development" && !r && (console.log( "For syntax highlighting of code blocks, you must provide a `createCodeBlockSpec({ createHighlighter: () => ... })` function" ), r = !0), []; if (!n) return t[j] = t[j] || e.createHighlighter(), t[j].then( (c) => { n = c; } ); const i = Te(e, s.language); return !i || i === "text" || i === "none" || i === "plaintext" || i === "txt" ? [] : n.getLoadedLanguages().includes(i) ? (o || (o = t[pe] || ut(n), t[pe] = o), o(s)) : n.loadLanguage(i); }, languageExtractor: (s) => s.attrs.language, nodeTypes: ["codeBlock"] }); } const Ut = ({ defaultLanguage: e = "text" }) => ({ type: "codeBlock", propSchema: { language: { default: e } }, content: "inline" }), qt = v( Ut, (e) => ({ meta: { code: !0, defining: !0, isolating: !1 }, parse: (t) => { var r, a; if (t.tagName !== "PRE" || t.childElementCount !== 1 || ((r = t.firstElementChild) == null ? void 0 : r.tagName) !== "CODE") return; const n = t.firstElementChild; return { language: n.getAttribute("data-language") || ((a = n.className.split(" ").find((s) => s.includes("language-"))) == null ? void 0 : a.replace("language-", "")) }; }, parseContent: ({ el: t, schema: n }) => { const o = xe.fromSchema(n), r = t.firstElementChild; return o.parse(r, { preserveWhitespace: "full", topNode: n.nodes.codeBlock.create() }).content; }, render(t, n) { const o = document.createDocumentFragment(), r = document.createElement("pre"), a = document.createElement("code"); r.appendChild(a); let s; if (e.supportedLanguages) { const i = document.createElement("select"); Object.entries(e.supportedLanguages ?? {}).forEach( ([u, { name: d }]) => { const p = document.createElement("option"); p.value = u, p.text = d, i.appendChild(p); } ), i.value = t.props.language || e.defaultLanguage || "text"; const c = (u) => { const d = u.target.value; n.updateBlock(t.id, { props: { language: d } }); }; i.addEventListener("change", c), s = () => i.removeEventListener("change", c); const l = document.createElement("div"); l.contentEditable = "false", l.appendChild(i), o.appendChild(l); } return o.appendChild(r), { dom: o, contentDOM: a, destroy: () => { s == null || s(); } }; }, toExternalHTML(t) { const n = document.createElement("pre"), o = document.createElement("code"); return o.className = `language-${t.props.language}`, o.dataset.language = t.props.language, n.appendChild(o), { dom: n, contentDOM: o }; } }), (e) => [ k({ key: "code-block-highlighter", prosemirrorPlugins: [$t(e)] }), k({ key: "code-block-keyboard-shortcuts", keyboardShortcuts: { Delete: ({ editor: t }) => t.transact((n) => { const { block: o } = t.getTextCursorPosition(); if (o.type !== "codeBlock") return !1; const { $from: r } = n.selection; return r.parent.textContent ? !1 : (t.removeBlocks([o]), !0); }), Tab: ({ editor: t }) => e.indentLineWithTab === !1 ? !1 : t.transact((n) => { const { block: o } = t.getTextCursorPosition(); return o.type === "codeBlock" ? (n.insertText(" "), !0) : !1; }), Enter: ({ editor: t }) => t.transact((n) => { const { block: o, nextBlock: r } = t.getTextCursorPosition(); if (o.type !== "codeBlock") return !1; const { $from: a } = n.selection, s = a.parentOffset === a.parent.nodeSize - 2, i = a.parent.textContent.endsWith(` `); if (s && i) { if (n.delete(a.pos - 2, a.pos), r) return t.setTextCursorPosition(r, "start"), !0; const [c] = t.insertBlocks( [{ type: "paragraph" }], o, "after" ); return t.setTextCursorPosition(c, "start"), !0; } return n.insertText(` `), !0; }), "Shift-Enter": ({ editor: t }) => t.transact(() => { const { block: n } = t.getTextCursorPosition(); if (n.type !== "codeBlock") return !1; const [o] = t.insertBlocks( // insert a new paragraph [{ type: "paragraph" }], n, "after" ); return t.setTextCursorPosition(o, "start"), !0; }) }, inputRules: [ { find: /^```(.*?)\s$/, replace: ({ match: t }) => { const n = t[1].trim(); return { type: "codeBlock", props: { language: { language: Te(e, n) ?? n }.language }, content: [] }; } } ] }) ] ); function Te(e, t) { var n; return (n = Object.entries(e.supportedLanguages ?? {}).find( ([o, { aliases: r }]) => (r == null ? void 0 : r.includes(t)) || o === t )) == null ? void 0 : n[0]; } const jt = () => ({ type: "divider", propSchema: {}, content: "none" }), Gt = v( jt, { meta: { isolating: !1 }, parse(e) { if (e.tagName === "HR") return {}; }, render() { return { dom: document.createElement("hr") }; } }, [ k({ key: "divider-block-shortcuts", inputRules: [ { find: new RegExp("^---$"), replace() { return { type: "divider", props: {}, content: [] }; } } ] }) ] ), fe = (e) => ({ url: e.src || void 0 }), Zt = () => ({ type: "file", propSchema: { backgroundColor: h.backgroundColor, // File name. name: { default: "" }, // File url. url: { default: "" }, // File caption. caption: { default: "" } }, content: "none" }), zt = () => (e) => { if (e.tagName === "EMBED") { if (e.closest("figure")) return; const { backgroundColor: t } = y(e); return { ...fe(e), backgroundColor: t }; } if (e.tagName === "FIGURE") { const t = $(e, "embed"); if (!t) return; const { targetElement: n, caption: o } = t, { backgroundColor: r } = y(e); return { ...fe(n), backgroundColor: r, caption: o }; } }, Xt = v(Zt, { meta: { fileBlockAccept: ["*/*"] }, parse: zt(), render(e, t) { return Q(e, t); }, toExternalHTML(e) { if (!e.props.url) { const n = document.createElement("p"); return n.textContent = "Add file", { dom: n }; } const t = document.createElement("a"); return t.href = e.props.url, t.textContent = e.props.name || e.props.url, e.props.caption ? U(t, e.props.caption) : { dom: t }; } }), Kt = { set: (e, t) => window.localStorage.setItem( `toggle-${e.id}`, t ? "true" : "false" ), get: (e) => window.localStorage.getItem(`toggle-${e.id}`) === "true" }, Be = (e, t, n, o = Kt) => { if ("isToggleable" in e.props && !e.props.isToggleable) return { dom: n }; const r = document.createElement("div"), a = document.createElement("div"); a.className = "bn-toggle-wrapper"; const s = document.createElement("button"); s.className = "bn-toggle-button", s.type = "button", s.innerHTML = // https://fonts.google.com/icons?selected=Material+Symbols+Rounded:chevron_right:FILL@0;wght@700;GRAD@0;opsz@24&icon.query=chevron&icon.style=Rounded&icon.size=24&icon.color=%23e8eaed '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="CURRENTCOLOR"><path d="M320-200v-560l440 280-440 280Z"/></svg>'; const i = (f) => f.preventDefault(); s.addEventListener("mousedown", i); const c = () => { var f; a.getAttribute("data-show-children") === "true" ? (a.setAttribute("data-show-children", "false"), o.set(t.getBlock(e), !1), r.contains(l) && r.removeChild(l)) : (a.setAttribute("data-show-children", "true"), o.set(t.getBlock(e), !0), t.isEditable && ((f = t.getBlock(e)) == null ? void 0 : f.children.length) === 0 && !r.contains(l) && r.appendChild(l)); }; s.addEventListener("click", c), a.appendChild(s), a.appendChild(n); const l = document.createElement("button"); l.className = "bn-toggle-add-block-button", l.type = "button", l.textContent = t.dictionary.toggle_blocks.add_block_button; const u = (f) => f.preventDefault(); l.addEventListener( "mousedown", u ); const d = () => { t.transact(() => { const f = t.updateBlock(e, { // Single empty block with default type. children: [{}] }); t.setTextCursorPosition(f.children[0].id, "end"), t.focus(); }); }; l.addEventListener("click", d), r.appendChild(a); let p = e.children.length; const m = t.onChange(() => { var E; const f = ((E = t.getBlock(e)) == null ? void 0 : E.children.length) ?? 0; f > p ? (a.getAttribute("data-show-children") === "false" && (a.setAttribute("data-show-children", "true"), o.set(t.getBlock(e), !0)), r.contains(l) && r.removeChild(l)) : f === 0 && f < p && (a.getAttribute("data-show-children") === "true" && (a.setAttribute("data-show-children", "false"), o.set(t.getBlock(e), !1)), r.contains(l) && r.removeChild(l)), p = f; }); return o.get(e) ? (a.setAttribute("data-show-children", "true"), t.isEditable && e.children.length === 0 && r.appendChild(l)) : a.setAttribute("data-show-children", "false"), { dom: r, // Prevents re-renders when the toggle button is clicked. ignoreMutation: (f) => f instanceof MutationRecord && // We want to prevent re-renders when the view changes, so we ignore // all mutations where the `data-show-children` attribute is changed // or the "add block" button is added/removed. (f.type === "attributes" && f.target === a && f.attributeName === "data-show-children" || f.type === "childList" && (f.addedNodes[0] === l || f.removedNodes[0] === l)), destroy: () => { s.removeEventListener("mousedown", i), s.removeEventListener("click", c), l.removeEventListener( "mousedown", u ), l.removeEventListener( "click", d ), m == null || m(); } }; }, Ae = [1, 2, 3, 4, 5, 6], Qt = (e) => ({ editor: t }) => { const n = t.getTextCursorPosition(); return t.schema.blockSchema[n.block.type].content !== "inline" ? !1 : (t.updateBlock(n.block, { type: "heading", props: { level: e } }), !0); }, Jt = ({ defaultLevel: e = 1, levels: t = Ae, allowToggleHeadings: n = !0 } = {}) => ({ type: "heading", propSchema: { ...h, level: { default: e, values: t }, ...n ? { isToggleable: { default: !1, optional: !0 } } : {} }, content: "inline" }), Yt = v( Jt, ({ allowToggleHeadings: e = !0 } = {}) => ({ meta: { isolating: !1 }, parse(t) { let n; switch (t.tagName) { case "H1": n = 1; break; case "H2": n = 2; break; case "H3": n = 3; break; case "H4": n = 4; break; case "H5": n = 5; break; case "H6": n = 6; break; default: return; } return { ...y(t), level: n }; }, render(t, n) { const o = document.createElement(`h${t.props.level}`); return e ? { ...Be(t, n, o), contentDOM: o } : { dom: o, contentDOM: o }; }, toExternalHTML(t) { const n = document.createElement(`h${t.props.level}`); return A(t.props, n), { dom: n, contentDOM: n }; } }), ({ levels: e = Ae } = {}) => [ k({ key: "heading-shortcuts", keyboardShortcuts: Object.fromEntries( e.map((t) => [ `Mod-Alt-${t}`, Qt(t) ]) ?? [] ), inputRules: e.map((t) => ({ find: new RegExp(`^(#{${t}})\\s$`), replace({ match: n }) { return { type: "heading", props: { level: n[1].length } }; } })) }) ] ), Pe = (e, t, n, o, r) => { const { dom: a, destroy: s } = Q( e, t, n, r ), i = a; i.style.position = "relative", e.props.url && e.props.showPreview && (e.props.previewWidth ? i.style.width = `${e.props.previewWidth}px` : i.style.width = "fit-content"); const c = document.createElement("div"); c.className = "bn-resize-handle", c.style.left = "4px"; const l = document.createElement("div"); l.className = "bn-resize-handle", l.style.right = "4px"; const u = document.createElement("div"); u.style.position = "absolute", u.style.height = "100%", u.style.width = "100%"; let d, p = e.props.previewWidth; const m = (g) => { var ee, te; if (!d) { !t.isEditable && o.contains(c) && o.contains(l) && (o.removeChild(c), o.removeChild(l)); return; } let S; const _ = "touches" in g ? g.touches[0].clientX : g.clientX; e.props.textAlignment === "center" ? d.handleUsed === "left" ? S = d.initialWidth + (d.initialClientX - _) * 2 : S = d.initialWidth + (_ - d.initialClientX) * 2 : d.handleUsed === "left" ? S = d.initialWidth + d.initialClientX - _ : S = d.initialWidth + _ - d.initialClientX, p = Math.min( Math.max(S, 64), ((te = (ee = t.domElement) == null ? void 0 : ee.firstElementChild) == null ? void 0 : te.clientWidth) || Number.MAX_VALUE ), i.style.width = `${p}px`; }, f = (g) => { (!g.target || !i.contains(g.target) || !t.isEditable) && o.contains(c) && o.contains(l) && (o.removeChild(c), o.removeChild(l)), d && (d = void 0, i.contains(u) && i.removeChild(u), t.updateBlock(e, { props: { previewWidth: p } })); }, E = () => { t.isEditable && (o.appendChild(c), o.appendChild(l)); }, w = (g) => { g.relatedTarget === c || g.relatedTarget === l || d || t.isEditable && o.contains(c) && o.contains(l) && (o.removeChild(c), o.removeChild(l)); }, x = (g) => { g.preventDefault(), i.contains(u) || i.appendChild(u); const S = "touches" in g ? g.touches[0].clientX : g.clientX; d = { handleUsed: "left", initialWidth: i.clientWidth, initialClientX: S }; }, P = (g) => { g.preventDefault(), i.contains(u) || i.appendChild(u); const S = "touches" in g ? g.touches[0].clientX : g.clientX; d = { handleUsed: "right", initialWidth: i.clientWidth, initialClientX: S }; }; return window.addEventListener("mousemove", m), window.addEventListener("touchmove", m), window.addEventListener("mouseup", f), window.addEventListener("touchend", f), i.addEventListener("mouseenter", E), i.addEventListener("mouseleave", w), c.addEventListener( "mousedown", x ), c.addEventListener( "touchstart", x ), l.addEventListener( "mousedown", P ), l.addEventListener( "touchstart", P ), { dom: i, destroy: () => { s == null || s(), window.removeEventListener("mousemove", m), window.removeEventListener("touchmove", m), window.removeEventListener("mouseup", f), window.removeEventListener("touchend", f), i.removeEventListener("mouseenter", E), i.removeEventListener("mouseleave", w), c.removeEventListener( "mousedown", x ), c.removeEventListener( "touchstart", x ), l.removeEventListener( "mousedown", P ), l.removeEventListener( "touchstart", P ); } }; }, he = (e) => { const t = e.src || void 0, n = e.width || void 0, o = e.alt || void 0; return { url: t, previewWidth: n, name: o }; }, en = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 11.1005L7 9.1005L12.5 14.6005L16 11.1005L19 14.1005V5H5V11.1005ZM4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3ZM15.5 10C14.6716 10 14 9.32843 14 8.5C14 7.67157 14.6716 7 15.5 7C16.3284 7 17 7.67157 17 8.5C17 9.32843 16.3284 10 15.5 10Z"></path></svg>', tn = (e = {}) => ({ type: "image", propSchema: { textAlignment: h.textAlignment, backgroundColor: h.backgroundColor, // File name. name: { default: "" }, // File url. url: { default: "" }, // File caption. caption: { default: "" }, showPreview: { default: !0 }, // File preview width in px. previewWidth: { default: void 0, type: "number" } }, content: "none" }), nn = (e = {}) => (t) => { if (t.tagName === "IMG") { if (t.closest("figure")) return; const { backgroundColor: n } = y(t); return { ...he(t), backgroundColor: n }; } if (t.tagName === "FIGURE") { const n = $(t, "img"); if (!n) return; const { targetElement: o, caption: r } = n, { backgroundColor: a } = y(t); return { ...he(o), backgroundColor: a, caption: r }; } }, on = (e = {}) => (t, n) => { const o = document.createElement("div"); o.innerHTML = e.icon ?? en; const r = document.createElement("div"); r.className = "bn-visual-media-wrapper"; const a = document.createElement("img"); return a.className = "bn-visual-media", n.resolveFileUrl ? n.resolveFileUrl(t.props.url).then((s) => { a.src = s; }) : a.src = t.props.url, a.alt = t.props.name || t.props.caption || "BlockNote image", a.contentEditable = "false", a.draggable = !1, r.appendChild(a), Pe( t, n, { dom: r }, r, o.firstElementChild ); }, rn = (e = {}) =>