@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
992 lines (991 loc) • 35.3 kB
JavaScript
import { It as e, O as t, wn as n } from "./blocks-CzQLehlc.js";
import { a as r, n as i, t as a } from "./BlockNoteExtension-CbvDA0F7.js";
import { _ as o, b as s, x as c, y as l } from "./src-BkfZVfaO.js";
import { a as u, i as d, o as f } from "./extensions-DGuhtOgB.js";
import { n as p } from "./UserStore-C4TzkGEn.js";
import { t as m } from "./ThreadStore-et9wB5Xt.js";
import { Mark as h, getChangedRanges as g, getMarkRange as _, posToDOMRect as ee } from "@tiptap/core";
import { uint32 as te, uuidv4 as v } from "lib0/random";
import { Plugin as ne, PluginKey as re } from "prosemirror-state";
import { acceptAllChanges as ie, acceptChanges as ae, configureYProsemirror as y, defaultSelectionBuilder as oe, deltaAttributionToFormat as se, deltaToPNode as ce, deltaToPSteps as le, docToDelta as b, nodeToDelta as x, pauseSync as ue, pmToFragment as de, rejectAllChanges as fe, rejectChanges as pe, relativePositionStore as me, syncPlugin as he, yCursorPlugin as ge, ySyncPluginKey as S } from "@y/prosemirror";
import * as C from "lib0/delta";
import * as w from "@y/y";
import { decodeAny as T, encodeAny as E } from "lib0/buffer";
//#region src/y/extensions/YAttributionMarks.ts
var D = (e, t) => e === void 0 ? void 0 : typeof e == "string" ? e : e[t], O = (e, t) => ({ mark: n, inline: r }) => {
let i = t?.editor, a = e === "insert" ? "ins" : e === "delete" ? "del" : r ? "span" : "div", o = document.createElement(a);
Object.assign(o.dataset, {
userIds: JSON.stringify(n.attrs.userIds),
inline: String(r)
}), e === "modification" && (o.dataset.type = "modification", o.dataset.format = JSON.stringify(n.attrs.format));
let u = D(t?.getAttributionMarkClassName?.({
contentType: r ? "inline-content" : "block",
modificationType: e === "modification" ? "format" : e
}), "content"), d = (n.attrs.userIds ?? [])[0], f = d ? l(d) : s[0];
if (u) o.style.cssText = "display: contents";
else {
let e = d ? `var(${c(d).light}, ${f.light})` : f.light, t = d ? `var(${c(d).dark}, ${f.dark})` : f.dark;
o.style.cssText = `display: contents; --user-color-light: ${e}; --user-color-dark: ${t}`;
}
let p = document.createElement("span");
if (r) p.className = u ?? (e === "delete" ? "bn-suggestion-mark bn-suggestion-mark--delete" : "bn-suggestion-mark");
else if (p.style.display = "contents", p.className = u ?? (e === "delete" ? "bn-suggestion-node bn-suggestion-node--delete" : "bn-suggestion-node"), e === "delete") {
let e = i?.dictionary.suggestion_changes.deleted;
e && p.style.setProperty("--deleted-label", JSON.stringify(e));
}
return o.appendChild(p), {
dom: o,
contentDOM: p
};
}, k = h.create({
name: "y-attributed-insert",
inclusive: !1,
excludes: "",
group: `${t} ${r}`,
addAttributes() {
return { userIds: { default: null } };
},
addMarkView() {
return O("insert", { getAttributionMarkClassName: this.options.getAttributionMarkClassName });
},
extendMarkSchema(e) {
return e.name === this.name ? { blocknoteIgnore: !0 } : {};
}
}), _e = h.create({
name: "y-attributed-delete",
inclusive: !1,
excludes: "",
group: `${t} ${r}`,
addAttributes() {
return { userIds: { default: null } };
},
addMarkView() {
return O("delete", {
editor: this.options.editor,
getAttributionMarkClassName: this.options.getAttributionMarkClassName
});
},
extendMarkSchema(e) {
return e.name === this.name ? { blocknoteIgnore: !0 } : {};
}
}), A = h.create({
name: "y-attributed-format",
inclusive: !1,
excludes: "",
group: `${t} ${r}`,
addAttributes() {
return {
userIds: { default: null },
format: { default: null }
};
},
addMarkView() {
return O("modification", { getAttributionMarkClassName: this.options.getAttributionMarkClassName });
},
extendMarkSchema(e) {
return e.name === this.name ? { blocknoteIgnore: !0 } : {};
}
}), j = a(({ options: e }) => ({
key: "yAttributionMarks",
tiptapExtensions: [
k.configure({ getAttributionMarkClassName: e?.getAttributionMarkClassName }),
_e.configure({ getAttributionMarkClassName: e?.getAttributionMarkClassName }),
A.configure({ getAttributionMarkClassName: e?.getAttributionMarkClassName })
]
})), ve = {
"y-attributed-insert": "insert",
"y-attributed-delete": "delete",
"y-attributed-format": "format"
}, ye = new re("attributionLoadUsers"), M = "[data-user-ids]", N = (e) => {
if (!e) return [];
let t;
try {
t = JSON.parse(e);
} catch {
return [];
}
return Array.isArray(t) ? t.map(String) : [];
}, P = (e) => {
if (!e) return [];
let t;
try {
t = JSON.parse(e);
} catch {
return [];
}
return typeof t != "object" || !t ? [] : Object.keys(t);
}, F = (e) => {
let t = e.firstElementChild ?? e, n = t.getBoundingClientRect();
return n.width || n.height ? t : t.firstElementChild ?? t;
}, be = (e) => F(e).getBoundingClientRect(), xe = (e) => F(e).getClientRects(), I = a(({ options: e }) => {
let t = p(e?.resolveUsers), n = e?.getAttributionMarkClassName, r = i(void 0), a = (e) => {
let n = g(e);
if (n.length === 0) return;
let r = Infinity, i = -Infinity;
for (let { newRange: e } of n) r = Math.min(r, e.from), i = Math.max(i, e.to);
let a = /* @__PURE__ */ new Set();
e.doc.nodesBetween(r, i, (e) => {
for (let t of e.marks) ve[t.type.name] && t.attrs.userIds?.forEach((e) => a.add(e));
return !0;
}), a.size > 0 && t.loadUsers(Array.from(a));
};
return {
key: "attribution",
userStore: t,
store: r,
prosemirrorPlugins: [new ne({
key: ye,
state: {
init: () => null,
apply: (e) => (e.docChanged && a(e), null)
}
})],
mount({ dom: e, root: i, signal: a }) {
let s = () => {
for (let [n, r] of t.store.state) {
let { light: t, dark: i } = c(n);
r.color && r.colorLight ? (e.style.setProperty(t, r.colorLight), e.style.setProperty(i, r.color)) : (e.style.removeProperty(t), e.style.removeProperty(i));
}
}, l, u = (e) => N(e).map((e) => t.getUser(e)?.username ?? e), d = (e) => {
let t = N(e.dataset.userIds);
return t.length === 0 ? "" : `${P(e.dataset.format).join(",")}:${t.join(",")}`;
}, f = (e) => {
let r = e.dataset.format !== void 0, i = r ? "format" : e.tagName === "INS" ? "insert" : "delete", a = e.dataset.inline === "false" ? "block" : "inline-content";
return {
anchor: e,
color: o(t, N(e.dataset.userIds)).dark,
modificationType: i,
contentType: a,
users: u(e.dataset.userIds),
format: r ? P(e.dataset.format) : void 0,
className: D(n?.({
contentType: a,
modificationType: i
}), "tooltip")
};
}, p = () => {
l && (l = void 0, r.setState(void 0));
}, m = (e) => {
for (; e;) {
let t = e.closest(M);
if (!t) return;
if (d(t)) return t;
e = t.parentElement;
}
};
i.addEventListener("mouseover", (e) => {
let n = e.target instanceof Element ? e.target : null, i = m(n);
if (!i) {
p();
return;
}
let a = d(i), o = i, s = i.parentElement;
for (; s;) {
let e = s.closest(M);
if (!e) break;
let t = d(e);
if (t === a) o = e;
else if (t) break;
s = e.parentElement;
}
if (l === o) return;
l = o, r.setState(f(o));
let c = N(o.dataset.userIds);
c.length > 0 && t.loadUsers(c).then(() => {
l === o && r.setState(f(o));
});
}, { signal: a }), a.addEventListener("abort", p), s();
let h = t.store.subscribe(s);
a.addEventListener("abort", h);
},
blockNoteExtensions: [j({ getAttributionMarkClassName: e?.getAttributionMarkClassName })]
};
}), L = a(({ editor: e }) => ({
key: "yPositionMapping",
mapPosition: (t, n = "left") => {
let r = S.getState(e.prosemirrorState);
if (!r?.ytype) throw Error("YSync plugin state not found");
if (t === 0) return () => 0;
let i = me(e.prosemirrorState.doc.resolve(t + (n === "right" ? 1 : -1)), r.ytype, r.renderer);
return () => {
let t = S.getState(e.prosemirrorState), r = i(e.prosemirrorState.doc, t.ytype, t.renderer);
if (r === null) throw Error("Position not found, cannot track positions");
return r + (n === "right" ? -1 : 1);
};
}
})), R = (e) => {
for (let t of e.children) if (C.$insertOp.check(t)) {
for (let e of t.insert) if (C.$deltaAny.check(e)) return e;
}
return null;
}, z = (e) => {
for (let t of e.children) if (C.$insertOp.check(t)) {
for (let e of t.insert) if (C.$deltaAny.check(e) && e.name === "blockGroup") return !0;
}
return !1;
};
function B(e) {
if (e.name !== "table") return null;
let t = [];
for (let n of e.children) if (C.$insertOp.check(n)) for (let e of n.insert) {
if (e.name !== "tableRow") return null;
let n = [];
for (let t of e.children) if (C.$insertOp.check(t)) for (let e of t.insert) {
if (e.name !== "tableCell" && e.name !== "tableHeader") return null;
n.push({
colspan: Number(e.attrs.colspan) || 1,
rowspan: Number(e.attrs.rowspan) || 1
});
}
t.push(n);
}
if (t.length === 0) return null;
let n = [];
for (let e = 0; e < t.length; e++) {
n[e] || (n[e] = []);
let r = 0;
for (let i of t[e]) {
for (; n[e][r];) r++;
for (let t = 0; t < i.rowspan; t++) {
n[e + t] || (n[e + t] = []);
for (let a = 0; a < i.colspan; a++) n[e + t][r + a] = !0;
}
r += i.colspan;
}
}
let r = Math.max(...n.map((e) => e.length));
return {
rows: t.length,
cols: r
};
}
var V = (e, t) => {
if (e.name !== t.name) return !1;
if (e.name !== "blockContainer") return !0;
let n = R(e), r = R(t);
if (n?.name !== r?.name || z(e) !== z(t)) return !1;
if (n?.name === "table" && r?.name === "table") {
let e = B(n), t = B(r);
if (e !== null && t !== null && e.rows !== t.rows && e.cols !== t.cols) return !1;
}
return !0;
}, H = (e, t) => {
let n = { ...e };
return t.insert && (n["y-attributed-insert"] = { userIds: t.insert }), t.delete && (n["y-attributed-delete"] = { userIds: t.delete }), t.format && (n["y-attributed-format"] = {
userIds: [...new Set(Object.values(t.format).flat())],
format: t.format
}), n;
}, U = a(({ options: e, editor: t }) => ({
key: "ySync",
fragment: e.fragment,
mount: () => {
let n = () => {
t.exec(y({ ytype: e.fragment }));
};
if (e.provider && "synced" in e.provider && typeof e.provider.synced == "boolean") {
if (e.provider.synced) n();
else if ("on" in e.provider && typeof e.provider.on == "function") e.provider.on("synced", (e) => {
e && n();
});
else throw Error("YSyncExtension: provider must have a 'synced' boolean or an 'on' method to listen for 'sync'");
} else n();
},
prosemirrorPlugins: [he({
suggestionDoc: e.suggestionDoc,
mapAttributionToMark: H,
customCompare: V
})],
runsBefore: ["default"]
}));
//#endregion
//#region src/y/utils.ts
function W(e, t) {
let n = e.doc;
if (!n) throw Error("type does not have a ydoc");
if (e._item === null) {
let r = Array.from(n.share.keys()).find((t) => n.share.get(t) === e);
if (r == null) throw Error("type does not exist in other ydoc");
return t.get(r, e.constructor);
}
{
let n = e._item, r = t.store.clients.get(n.id.client) ?? [], i = r[w.findIndexSS(r, n.id.clock)];
if (!i) throw Error("type does not exist in other ydoc");
let a = i.content;
if (!a) throw Error("type does not exist in other ydoc");
return a.type;
}
}
function Se(e, t) {
let r = e.pmSchema.nodeFromJSON(t);
return n(r);
}
function G(t, n) {
let r = n.map((n) => e(n, t.pmSchema));
return t.pmSchema.topNodeType.create(null, t.pmSchema.nodes.blockGroup.create(null, r));
}
function K(e, t) {
let r = ce(t.toDeltaDeep(), e.pmSchema, null);
return r === null ? [] : n(r);
}
function Ce(e, t, n) {
return n ||= new w.Doc().get("prosemirror"), de(G(e, t), n);
}
function we(e, t, n = "prosemirror") {
return K(e, t.get(n));
}
function Te(e, t, n = "prosemirror") {
let r = b(G(e, t)), i = new w.Doc();
return i.get(n).applyDelta(r), i;
}
function q(e, t) {
let n = x(e), r = x(t);
return C.diff(n.done(), r.done(), { compare: V });
}
function J({ tr: e, fragment: t, renderer: n }) {
let r = se(t.toDeltaDeep({ renderer: n }), H), i = x(e.doc, void 0, !0), a = C.diff(i.done(), r.done(), { compare: V });
return le(e, a, void 0, void 0);
}
//#endregion
//#region src/y/extensions/Suggestions.ts
var Ee = a(({ editor: e, options: t }) => {
let n = t.suggestionDoc;
if (!n) throw Error("Suggestion doc not found");
let r = p(t.resolveUsers);
function i(t) {
let n = e.prosemirrorView.nodeDOM(t);
for (; n && n.parentElement;) {
if (n.nodeName === "INS" || n.nodeName === "DEL") return n;
n = n.parentElement;
}
return null;
}
function a(t, n) {
return e.transact((r) => {
let i = r.doc.resolve(t), a = i.marks().find((e) => e.type.name === n);
if (!a) return;
let o = _(i, a.type);
if (o) return {
range: o,
mark: a,
get text() {
return r.doc.textBetween(o.from, o.to);
},
get position() {
return ee(e.prosemirrorView, o.from, o.to).toJSON();
}
};
});
}
function o() {
return e.transact((e) => {
let t = e.selection;
if (t.empty) return a(t.anchor, "insertion") || a(t.anchor, "deletion") || a(t.anchor, "modification");
});
}
return {
key: "suggestions",
userStore: r,
runsBefore: ["ySync"],
viewSuggestions: () => {
t.renderer && (t.renderer.suggestionMode = !1), e.exec(y({
ytype: W(t.fragment, n),
renderer: t.renderer
}));
},
enableSuggestions: () => {
t.renderer && (t.renderer.suggestionMode = !0), e.exec(y({
ytype: W(t.fragment, n),
renderer: t.renderer
}));
},
disableSuggestions: () => {
e.exec(y({
ytype: t.fragment,
renderer: null
}));
},
applyAllSuggestions: () => e.exec(ie()),
applySuggestion: (t, n) => e.exec(ae(t, n)),
revertSuggestion: (t, n) => e.exec(pe(t, n)),
revertAllSuggestions: () => e.exec(fe()),
getSuggestionElementAtPos: i,
getMarkAtPos: a,
getSuggestionAtSelection: o,
getSuggestionAtCoords: (t) => e.transact(() => {
let n = e.prosemirrorView.posAtCoords(t);
if (n !== null && n?.inside !== -1) return a(n.pos, "y-attributed-insert") || a(n.pos, "y-attributed-delete") || a(n.pos, "y-attributed-format");
}),
checkUnresolvedSuggestions: () => {
let t = !1;
return e.prosemirrorState.doc.descendants((e) => !t && (t = e.marks.findIndex((e) => e.type.name === "y-attributed-insert" || e.type.name === "y-attributed-delete" || e.type.name === "y-attributed-format") !== -1, !0)), t;
}
};
});
//#endregion
//#region src/y/extensions/Versioning.ts
function De(e) {
e.exec(ue), e.removeBlocks(e.document);
}
function Oe(e, t) {
return {
getCurrentDocument: () => t,
serializeCurrentContent: () => w.encodeStateAsUpdateV2(t.doc),
preview: {
enterPreview: (n, r, i) => {
let a;
if (r) {
let e = new w.Doc({ isSuggestionDoc: !0 });
w.applyUpdateV2(e, r), a = { fragment: W(t, e) };
}
let o = new w.Doc();
w.applyUpdateV2(o, n), De(e), e.exec((e, n) => {
let r = J({
tr: e.tr,
fragment: W(t, o),
renderer: a ? w.createDiffRenderer(a.fragment.doc, o, i ? { attrs: i } : void 0) : void 0
});
return n && n(r), !0;
});
},
exitPreview: () => {
De(e), e.exec(y({ ytype: t }));
},
applyRestore: (e) => {}
}
};
}
//#endregion
//#region src/y/extensions/YCursorPlugin.ts
function ke(e) {
let t = e.charAt(0) === "#" ? e.substring(1, 7) : e, n = parseInt(t.substring(0, 2), 16), r = parseInt(t.substring(2, 4), 16), i = parseInt(t.substring(4, 6), 16), a = [
n / 255,
r / 255,
i / 255
].map((e) => e <= .03928 ? e / 12.92 : ((e + .055) / 1.055) ** 2.4);
return .2126 * a[0] + .7152 * a[1] + .0722 * a[2] <= .179;
}
function Ae(e) {
let t = document.createElement("span");
t.classList.add("bn-collaboration-cursor__base");
let n = document.createElement("span");
n.setAttribute("contentedEditable", "false"), n.classList.add("bn-collaboration-cursor__caret"), n.setAttribute("style", `background-color: ${e.color}; color: ${ke(e.color) ? "white" : "black"}`);
let r = document.createElement("span");
return r.classList.add("bn-collaboration-cursor__label"), r.setAttribute("style", `background-color: ${e.color}; color: ${ke(e.color) ? "white" : "black"}`), r.insertBefore(document.createTextNode(e.name), null), n.insertBefore(r, null), t.insertBefore(document.createTextNode(""), null), t.insertBefore(n, null), t.insertBefore(document.createTextNode(""), null), t;
}
var je = a(({ options: e }) => {
let t = /* @__PURE__ */ new Map(), n = e.provider && "awareness" in e.provider && typeof e.provider.awareness == "object" ? e.provider.awareness : void 0;
return n && ("setLocalStateField" in n && typeof n.setLocalStateField == "function" && n.setLocalStateField("user", e.user), "on" in n && typeof n.on == "function" && e.showCursorLabels !== "always" && n.on("change", ({ updated: e }) => {
for (let n of e) {
let e = t.get(n);
e && (setTimeout(() => {
e.element.setAttribute("data-active", "");
}, 10), e.hideTimeout && clearTimeout(e.hideTimeout), t.set(n, {
element: e.element,
hideTimeout: setTimeout(() => {
e.element.removeAttribute("data-active");
}, 2e3)
}));
}
})), {
key: "yCursor",
prosemirrorPlugins: [n ? ge(n, {
selectionBuilder: oe,
cursorBuilder(n, r) {
let i = t.get(r);
if (!i) {
let a = (e.renderCursor ?? Ae)(n);
e.showCursorLabels !== "always" && (a.addEventListener("mouseenter", () => {
let e = t.get(r);
e.element.setAttribute("data-active", ""), e.hideTimeout && (clearTimeout(e.hideTimeout), t.set(r, {
element: e.element,
hideTimeout: void 0
}));
}), a.addEventListener("mouseleave", () => {
let e = t.get(r);
t.set(r, {
element: e.element,
hideTimeout: setTimeout(() => {
e.element.removeAttribute("data-active");
}, 2e3)
});
})), i = {
element: a,
hideTimeout: void 0
}, t.set(r, i);
}
return i.element;
}
}) : void 0].filter((e) => e !== void 0),
dependsOn: ["ySync"],
updateUser(e) {
n?.setLocalStateField("user", e);
},
getUser() {
let e = n?.getLocalState();
if (e) return e.user;
}
};
}), Me = "version:", Ne = (e) => Me + e, Pe = "This version", Fe = "#4363d8";
function Ie(e, t) {
let n = new w.Attributions();
return e.on("beforeObserverCalls", (e) => {
e.insertSet.isEmpty() || w.insertIntoIdMap(n.inserts, w.createIdMapFromIdSet(e.insertSet, [w.createContentAttribute("insert", t)])), e.deleteSet.isEmpty() || w.insertIntoIdMap(n.deletes, w.createIdMapFromIdSet(e.deleteSet, [w.createContentAttribute("delete", t)]));
}), n;
}
var Le = a(({ options: e, editor: t }) => {
let n = e?.color ?? Fe;
return {
key: "diffVersioning",
blockNoteExtensions: [I({
resolveUsers: async (e) => e.filter((e) => e.startsWith(Me)).map((e) => ({
id: e,
username: e.slice(8),
avatarUrl: "",
color: n
})),
getAttributionMarkClassName: e?.getAttributionMarkClassName
})],
renderDiff: (e, n, r = Pe) => {
let i = Ne(r);
if (!t.pmSchema.marks["y-attributed-insert"]) throw Error("DiffVersioningExtension: the y-attributed-* marks are missing from the schema. This should not happen — the extension registers them via AttributionExtension.");
let a = G(t, n), o = G(t, e), s = new w.Doc({ gc: !1 }), c = s.get("prosemirror");
s.transact(() => {
c.applyDelta(b(a));
});
let l = new w.Doc({ gc: !1 });
w.applyUpdateV2(l, w.encodeStateAsUpdateV2(s));
let u = W(c, l), d = Ie(l, i), f = q(a, o);
l.transact(() => {
u.applyDelta(f);
}, i);
let p = w.createDiffRenderer(s, l, { attrs: d });
t.replaceBlocks(t.document, []), t.exec((e, t) => {
let n = J({
tr: e.tr,
fragment: u,
renderer: p
});
return t && t(n), !0;
}), s.destroy(), l.destroy();
},
clearDiff: (e) => {
t.replaceBlocks(t.document, []), t.replaceBlocks(t.document, e);
}
};
}), Re = a(({ editor: e, options: t }) => {
let n = p(t.resolveUsers), r = {
...t,
resolveUsers: n
};
return {
key: "collaboration",
userStore: n,
blockNoteExtensions: [
t.suggestionDoc ? Ee(r) : null,
L(),
U(r),
je(t),
t.versioningEndpoints ? u({
...Oe(e, t.fragment),
endpoints: t.versioningEndpoints,
resolveUsers: n
}) : null,
I({
resolveUsers: n,
getAttributionMarkClassName: t.getAttributionMarkClassName
})
].filter((e) => e !== null)
};
});
function ze(e) {
return e.initialContent && console.warn("When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider"), {
...e,
extensions: [...e.extensions ?? [], Re(e.collaboration)],
disableExtensions: ["history", ...e.disableExtensions ?? []],
initialContent: [{
type: "paragraph",
id: "initialBlockId"
}]
};
}
//#endregion
//#region src/y/comments/yjsHelpers.ts
function Be(e) {
let t = new w.Type();
if (t.setAttr("id", e.id), t.setAttr("userId", e.userId), t.setAttr("createdAt", e.createdAt.getTime()), t.setAttr("updatedAt", e.updatedAt.getTime()), e.deletedAt ? (t.setAttr("deletedAt", e.deletedAt.getTime()), t.setAttr("body", void 0)) : t.setAttr("body", e.body), e.reactions.length > 0) throw Error("Reactions should be empty in commentToYType");
return t.setAttr("reactionsByUser", new w.Type()), t.setAttr("metadata", e.metadata), t;
}
function Ve(e) {
let t = new w.Type();
t.setAttr("id", e.id), t.setAttr("createdAt", e.createdAt.getTime()), t.setAttr("updatedAt", e.updatedAt.getTime());
let n = new w.Type();
return n.push(e.comments.map((e) => Be(e))), t.setAttr("comments", n), t.setAttr("resolved", e.resolved), t.setAttr("resolvedUpdatedAt", e.resolvedUpdatedAt?.getTime()), t.setAttr("resolvedBy", e.resolvedBy), t.setAttr("metadata", e.metadata), t;
}
function He(e) {
return {
emoji: e.getAttr("emoji"),
createdAt: new Date(e.getAttr("createdAt")),
userId: e.getAttr("userId")
};
}
function Ue(e) {
return [...e.attrValues()].map((e) => He(e)).reduce((e, t) => {
let n = e.find((e) => e.emoji === t.emoji);
return n ? (n.userIds.push(t.userId), n.createdAt = new Date(Math.min(n.createdAt.getTime(), t.createdAt.getTime()))) : e.push({
emoji: t.emoji,
createdAt: t.createdAt,
userIds: [t.userId]
}), e;
}, []);
}
function Y(e) {
return {
type: "comment",
id: e.getAttr("id"),
userId: e.getAttr("userId"),
createdAt: new Date(e.getAttr("createdAt")),
updatedAt: new Date(e.getAttr("updatedAt")),
deletedAt: e.getAttr("deletedAt") ? new Date(e.getAttr("deletedAt")) : void 0,
reactions: Ue(e.getAttr("reactionsByUser")),
metadata: e.getAttr("metadata"),
body: e.getAttr("body")
};
}
function X(e) {
return {
type: "thread",
id: e.getAttr("id"),
createdAt: new Date(e.getAttr("createdAt")),
updatedAt: new Date(e.getAttr("updatedAt")),
comments: (e.getAttr("comments")?.toArray() || []).map((e) => Y(e)),
resolved: e.getAttr("resolved"),
resolvedUpdatedAt: new Date(e.getAttr("resolvedUpdatedAt")),
resolvedBy: e.getAttr("resolvedBy"),
metadata: e.getAttr("metadata")
};
}
//#endregion
//#region src/y/comments/YjsThreadStoreBase.ts
var Z = class extends m {
threadsYType;
constructor(e, t) {
super(t), this.threadsYType = e;
}
getThread(e) {
let t = this.threadsYType.getAttr(e);
if (!t) throw Error("Thread not found");
return X(t);
}
getThreads() {
let e = /* @__PURE__ */ new Map();
return this.threadsYType.forEachAttr((t, n) => {
t instanceof w.Type && e.set(String(n), X(t));
}), e;
}
subscribe(e) {
let t = () => {
e(this.getThreads());
};
return this.threadsYType.observeDeep(t), () => {
this.threadsYType.unobserveDeep(t);
};
}
}, We = class extends Z {
BASE_URL;
headers;
constructor(e, t, n, r) {
super(n, r), this.BASE_URL = e, this.headers = t;
}
doRequest = async (e, t, n) => {
let r = await fetch(`${this.BASE_URL}${e}`, {
method: t,
body: JSON.stringify(n),
headers: {
"Content-Type": "application/json",
...this.headers
}
});
if (!r.ok) throw Error(`Failed to ${t} ${e}: ${r.statusText}`);
return r.json();
};
addThreadToDocument = async (e) => {
let { threadId: t, ...n } = e;
return this.doRequest(`/${t}/addToDocument`, "POST", n);
};
createThread = async (e) => this.doRequest("", "POST", e);
addComment = (e) => {
let { threadId: t, ...n } = e;
return this.doRequest(`/${t}/comments`, "POST", n);
};
updateComment = (e) => {
let { threadId: t, commentId: n, ...r } = e;
return this.doRequest(`/${t}/comments/${n}`, "PUT", r);
};
deleteComment = (e) => {
let { threadId: t, commentId: n, ...r } = e;
return this.doRequest(`/${t}/comments/${n}?soft=${!!r.softDelete}`, "DELETE");
};
deleteThread = (e) => this.doRequest(`/${e.threadId}`, "DELETE");
resolveThread = (e) => this.doRequest(`/${e.threadId}/resolve`, "POST");
unresolveThread = (e) => this.doRequest(`/${e.threadId}/unresolve`, "POST");
addReaction = (e) => {
let { threadId: t, commentId: n, ...r } = e;
return this.doRequest(`/${t}/comments/${n}/reactions`, "POST", r);
};
deleteReaction = (e) => this.doRequest(`/${e.threadId}/comments/${e.commentId}/reactions/${e.emoji}`, "DELETE");
}, Ge = class extends Z {
userId;
constructor(e, t, n) {
super(t, n), this.userId = e;
}
transact = (e) => async (t) => this.threadsYType.doc.transact(() => e(t));
createThread = this.transact((e) => {
if (!this.auth.canCreateThread()) throw Error("Not authorized");
let t = /* @__PURE__ */ new Date(), n = {
type: "comment",
id: v(),
userId: this.userId,
createdAt: t,
updatedAt: t,
reactions: [],
metadata: e.initialComment.metadata,
body: e.initialComment.body
}, r = {
type: "thread",
id: v(),
createdAt: t,
updatedAt: t,
comments: [n],
resolved: !1,
metadata: e.metadata
};
return this.threadsYType.setAttr(r.id, Ve(r)), r;
});
addThreadToDocument = void 0;
addComment = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
if (!this.auth.canAddComment(X(t))) throw Error("Not authorized");
let n = /* @__PURE__ */ new Date(), r = {
type: "comment",
id: v(),
userId: this.userId,
createdAt: n,
updatedAt: n,
deletedAt: void 0,
reactions: [],
metadata: e.comment.metadata,
body: e.comment.body
};
return t.getAttr("comments").push([Be(r)]), t.setAttr("updatedAt", (/* @__PURE__ */ new Date()).getTime()), r;
});
updateComment = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
let n = t.getAttr("comments"), r = Q(n, (t) => t.getAttr("id") === e.commentId);
if (r === -1) throw Error("Comment not found");
let i = n.get(r);
if (!this.auth.canUpdateComment(Y(i))) throw Error("Not authorized");
i.setAttr("body", e.comment.body), i.setAttr("updatedAt", (/* @__PURE__ */ new Date()).getTime()), i.setAttr("metadata", e.comment.metadata);
});
deleteComment = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
let n = t.getAttr("comments"), r = Q(n, (t) => t.getAttr("id") === e.commentId);
if (r === -1) throw Error("Comment not found");
let i = n.get(r);
if (!this.auth.canDeleteComment(Y(i))) throw Error("Not authorized");
if (i.getAttr("deletedAt")) throw Error("Comment already deleted");
e.softDelete ? (i.setAttr("deletedAt", (/* @__PURE__ */ new Date()).getTime()), i.setAttr("body", void 0)) : n.delete(r), n.toArray().every((e) => e.getAttr("deletedAt")) && (e.softDelete ? t.setAttr("deletedAt", (/* @__PURE__ */ new Date()).getTime()) : this.threadsYType.deleteAttr(e.threadId)), t.setAttr("updatedAt", (/* @__PURE__ */ new Date()).getTime());
});
deleteThread = this.transact((e) => {
if (!this.auth.canDeleteThread(X(this.threadsYType.getAttr(e.threadId)))) throw Error("Not authorized");
this.threadsYType.deleteAttr(e.threadId);
});
resolveThread = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
if (!this.auth.canResolveThread(X(t))) throw Error("Not authorized");
t.setAttr("resolved", !0), t.setAttr("resolvedUpdatedAt", (/* @__PURE__ */ new Date()).getTime()), t.setAttr("resolvedBy", this.userId);
});
unresolveThread = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
if (!this.auth.canUnresolveThread(X(t))) throw Error("Not authorized");
t.setAttr("resolved", !1), t.setAttr("resolvedUpdatedAt", (/* @__PURE__ */ new Date()).getTime());
});
addReaction = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
let n = t.getAttr("comments"), r = Q(n, (t) => t.getAttr("id") === e.commentId);
if (r === -1) throw Error("Comment not found");
let i = n.get(r);
if (!this.auth.canAddReaction(Y(i), e.emoji)) throw Error("Not authorized");
let a = /* @__PURE__ */ new Date(), o = `${this.userId}-${e.emoji}`, s = i.getAttr("reactionsByUser");
if (!s.hasAttr(o)) {
let t = new w.Type();
t.setAttr("emoji", e.emoji), t.setAttr("createdAt", a.getTime()), t.setAttr("userId", this.userId), s.setAttr(o, t);
}
});
deleteReaction = this.transact((e) => {
let t = this.threadsYType.getAttr(e.threadId);
if (!t) throw Error("Thread not found");
let n = t.getAttr("comments"), r = Q(n, (t) => t.getAttr("id") === e.commentId);
if (r === -1) throw Error("Comment not found");
let i = n.get(r);
if (!this.auth.canDeleteReaction(Y(i), e.emoji)) throw Error("Not authorized");
let a = `${this.userId}-${e.emoji}`;
i.getAttr("reactionsByUser").deleteAttr(a);
});
};
function Q(e, t) {
for (let n = 0; n < e.length; n++) if (t(e.get(n))) return n;
return -1;
}
//#endregion
//#region src/y/versioning/yhub.ts
var Ke = "__bn_version_names";
function qe(e) {
return e.customAttributions?.some((e) => e.k === "type" && e.v === "version") ?? !1;
}
function Je(e, t) {
let n = e.by?.split(",").map((e) => e.trim()).filter(Boolean), r = n && n.length > 0 ? n : void 0;
if (qe(e)) {
let t = e.customAttributions?.find((e) => e.k === "id")?.v;
return t === void 0 ? void 0 : {
id: t,
name: e.customAttributions?.find((e) => e.k === "name")?.v,
createdAt: e.to,
updatedAt: e.to,
by: r
};
}
return {
id: `history-${e.to}-${t}`,
createdAt: e.to,
updatedAt: e.to,
by: r
};
}
async function $(e, t, n) {
let r = await fetch(e, {
...n,
headers: {
...t,
...n?.headers instanceof Headers ? Object.fromEntries(n.headers.entries()) : Array.isArray(n?.headers) ? Object.fromEntries(n.headers) : n?.headers
}
});
if (!r.ok) throw Error(`YHub request failed: ${r.status} ${r.statusText} (${e})`);
return r.arrayBuffer();
}
function Ye(e) {
let { baseUrl: t, org: n, docId: r, headers: i = {}, activityLimit: a = 50, group: o } = e, s = `${t}/activity/${n}/${r}`, c = `${t}/changeset/${n}/${r}`, l = `${t}/rollback/${n}/${r}`;
return (u) => {
let p = () => (u.getExtension("ySync")?.fragment.doc)?.get(Ke), m = async () => {
let e = new URLSearchParams({
order: "desc",
limit: "1",
customAttributions: "true"
}), t = new URLSearchParams({
order: "desc",
limit: "1",
customAttributions: "true",
withCustomAttributions: "type:version"
}), [n, r] = await Promise.all([$(`${s}?${e}`, i), $(`${s}?${t}`, i)]), a = T(new Uint8Array(n))[0], o = T(new Uint8Array(r))[0];
if (!a || a.to <= (o?.to ?? 0)) return;
let c = a.by?.split(",").map((e) => e.trim()).filter(Boolean) ?? [];
return {
id: d,
createdAt: a.to,
updatedAt: a.to,
by: c.length > 0 ? c : void 0
};
}, h = async (e, a, o) => {
if (!e.doc) throw Error("Cannot patch document: the Y.Type is not attached to a Y.Doc.");
let s = new w.Doc();
s.get("__bn_version_markers", "XmlFragment").insert(0, ["v"]);
let c = {
update: w.encodeStateAsUpdate(s),
customAttributions: a
};
await $(`${t}/ydoc/${n}/${r}${o ? `?userid=${o}` : ""}`, i, {
method: "PATCH",
body: E(c)
});
}, g = async (e, t) => {
let n = String(te()), r = Date.now();
t?.name && p()?.setAttr(n, t.name);
let i = [{
k: "type",
v: "version"
}, {
k: "id",
v: n
}];
t?.name && i.push({
k: "name",
v: t.name
});
let a = u.getExtension("yCursor")?.getUser();
return await h(e, i, a?.id), {
id: n,
name: t?.name,
createdAt: r,
updatedAt: r,
by: a?.id
};
}, _ = async (e) => {
let t = new URLSearchParams({
ydoc: "true",
to: String(e)
}), n = await $(`${c}?${t}`, i), r = T(new Uint8Array(n));
if (!r.nextDoc) throw Error(`YHub returned no document state at timestamp ${e}.`);
return w.convertUpdateFormatV1ToV2(r.nextDoc);
};
return {
list: async () => {
let t = e.groupMaxGap ?? 1e4, n = e.groupMaxDuration, r = e.mergeUsers, c = new URLSearchParams({
order: "desc",
limit: String(a),
customAttributions: "true"
});
c.set("groupMaxGap", String(t)), o !== void 0 && c.set("group", String(o)), n !== void 0 && c.set("groupMaxDuration", String(n)), r !== void 0 && c.set("mergeUsers", String(r));
let l = await $(`${s}?${c}`, i), u = T(new Uint8Array(l)), d = f(u.map((e, t) => Je(e, t)).filter((e) => e !== void 0).map((e) => {
let t = e.name, n = (typeof e.id == "string" ? p()?.getAttr(e.id) : void 0) ?? t;
return {
...e,
name: n
};
})), h = await m();
return h ? [h, ...d] : d;
},
create: g,
getContent: async (e) => _(e.createdAt),
getAttributions: async (e, t) => {
let n = e.createdAt, r = t === void 0 ? 0 : t.createdAt, a = new URLSearchParams({
from: String(r),
to: String(n),
attributions: "true"
}), o = await $(`${c}?${a}`, i), s = T(new Uint8Array(o));
if (!s.attributions) throw Error(`YHub returned no attributions for snapshot ${String(e.id)}.`);
return w.decodeContentMap(s.attributions);
},
restore: async (e, t) => {
let n = t.createdAt, r = await _(n);
return await $(`${l}?from=${n}`, i, {
method: "POST",
body: E({ from: n })
}), r;
},
rename: async (e, t) => {
if (typeof e.id != "string") return;
let n = p();
if (!n) throw Error("Cannot rename version: no live collaboration document is available.");
t === void 0 || t === "" ? n.deleteAttr(e.id) : n.setAttr(e.id, t);
}
};
};
}
//#endregion
export { I as AttributionExtension, Re as CollaborationExtension, Le as DiffVersioningExtension, We as RESTYjsThreadStore, L as RelativePositionMappingExtension, Ee as SuggestionsExtension, _e as YAttributedDeletion, A as YAttributedFormat, k as YAttributedInsertion, j as YAttributionMarksExtension, je as YCursorExtension, U as YSyncExtension, Ge as YjsThreadStore, Z as YjsThreadStoreBase, G as _blocksToProsemirrorNode, Se as _prosemirrorJSONToBlocks, Te as blocksToYDoc, Ce as blocksToYType, Ye as createYHubVersioningEndpoints, Oe as createYjsVersioningAdapter, q as docDiffToDelta, W as findTypeInOtherYdoc, J as getProseMirrorTrFromYFragment, xe as getReferenceClientRects, be as getReferenceRect, H as mapAttributionToMark, D as resolveAttributionMarkClassName, ze as withCollaboration, we as yDocToBlocks, K as yfragmentToBlocks };
//# sourceMappingURL=y.js.map