@pixerate/editor 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -1
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent/index.cjs +175 -11
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -4
- package/dist/agent/index.d.ts +18 -4
- package/dist/agent/index.js +168 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
- package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
- package/dist/builtins-CdPcLwwF.d.cts +176 -0
- package/dist/builtins-Chj6zv_O.d.ts +176 -0
- package/dist/canvas/index.cjs +8588 -1857
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +1847 -9
- package/dist/canvas/index.d.ts +1847 -9
- package/dist/canvas/index.js +8577 -1846
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-3CC7OPUQ.cjs +831 -0
- package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
- package/dist/chunk-4BVP64JJ.cjs +1406 -0
- package/dist/chunk-4BVP64JJ.cjs.map +1 -0
- package/dist/chunk-BVP3TSLH.cjs +69 -0
- package/dist/chunk-BVP3TSLH.cjs.map +1 -0
- package/dist/{chunk-OLZJXL4R.cjs → chunk-CS6NZWWA.cjs} +59 -25
- package/dist/chunk-CS6NZWWA.cjs.map +1 -0
- package/dist/{chunk-MCSJFWAO.cjs → chunk-EWCOFSCQ.cjs} +685 -100
- package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
- package/dist/chunk-HDYMLS3X.cjs +409 -0
- package/dist/chunk-HDYMLS3X.cjs.map +1 -0
- package/dist/chunk-HRMEDT2L.js +1406 -0
- package/dist/chunk-HRMEDT2L.js.map +1 -0
- package/dist/{chunk-D27YFC46.cjs → chunk-IFHKKVXH.cjs} +382 -63
- package/dist/chunk-IFHKKVXH.cjs.map +1 -0
- package/dist/{chunk-PGNZKFQS.js → chunk-JOIREKL7.js} +137 -58
- package/dist/chunk-JOIREKL7.js.map +1 -0
- package/dist/chunk-KRGXUQ2Y.js +1696 -0
- package/dist/chunk-KRGXUQ2Y.js.map +1 -0
- package/dist/{chunk-3LV6RRPH.js → chunk-MHEPLBFB.js} +2 -2
- package/dist/chunk-MR2764X2.js +831 -0
- package/dist/chunk-MR2764X2.js.map +1 -0
- package/dist/{chunk-XNORY7M6.cjs → chunk-NFVBT3UZ.cjs} +166 -87
- package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
- package/dist/chunk-NQF32I6Z.cjs +6478 -0
- package/dist/chunk-NQF32I6Z.cjs.map +1 -0
- package/dist/chunk-O5RYH2PG.js +69 -0
- package/dist/chunk-O5RYH2PG.js.map +1 -0
- package/dist/{chunk-ITBSKI26.js → chunk-Q2OHWD2A.js} +1064 -360
- package/dist/chunk-Q2OHWD2A.js.map +1 -0
- package/dist/{chunk-FODNLX5N.js → chunk-QJSOZFCZ.js} +38 -4
- package/dist/chunk-QJSOZFCZ.js.map +1 -0
- package/dist/{chunk-SML64VJG.cjs → chunk-RRREAOVM.cjs} +4 -4
- package/dist/{chunk-SML64VJG.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
- package/dist/{chunk-5S3Z4PPF.js → chunk-SWE3NPOU.js} +347 -28
- package/dist/chunk-SWE3NPOU.js.map +1 -0
- package/dist/chunk-TXXNBDQX.cjs +1696 -0
- package/dist/chunk-TXXNBDQX.cjs.map +1 -0
- package/dist/chunk-UBCFIABU.js +409 -0
- package/dist/chunk-UBCFIABU.js.map +1 -0
- package/dist/chunk-X7HXL2SB.js +6478 -0
- package/dist/chunk-X7HXL2SB.js.map +1 -0
- package/dist/{chunk-SBCZ6RN5.cjs → chunk-YOMYSFJU.cjs} +1163 -459
- package/dist/chunk-YOMYSFJU.cjs.map +1 -0
- package/dist/{chunk-3CN2WD3X.js → chunk-ZRNHGAQT.js} +661 -76
- package/dist/chunk-ZRNHGAQT.js.map +1 -0
- package/dist/commands/index.cjs +425 -4
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +39 -77
- package/dist/commands/index.d.ts +39 -77
- package/dist/commands/index.js +424 -3
- package/dist/commands/index.js.map +1 -1
- package/dist/compare/index.cjs +3 -3
- package/dist/compare/index.d.cts +1 -1
- package/dist/compare/index.d.ts +1 -1
- package/dist/compare/index.js +2 -2
- package/dist/controller-CxXGOEmy.d.cts +2038 -0
- package/dist/controller-CxXGOEmy.d.ts +2038 -0
- package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
- package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
- package/dist/export/index.cjs +36 -0
- package/dist/export/index.cjs.map +1 -0
- package/dist/export/index.d.cts +243 -0
- package/dist/export/index.d.ts +243 -0
- package/dist/export/index.js +36 -0
- package/dist/export/index.js.map +1 -0
- package/dist/extensions/index.cjs +101 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +18 -5
- package/dist/extensions/index.d.ts +18 -5
- package/dist/extensions/index.js +101 -6
- package/dist/generation/index.cjs +1013 -69
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +474 -13
- package/dist/generation/index.d.ts +474 -13
- package/dist/generation/index.js +976 -32
- package/dist/generation/index.js.map +1 -1
- package/dist/image-editor/index.cjs +3385 -1275
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +693 -128
- package/dist/image-editor/index.d.ts +693 -128
- package/dist/image-editor/index.js +3325 -1215
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{index-fJrlkJCd.d.ts → index-B273Zllk.d.ts} +500 -7
- package/dist/{index-BhJBSm2d.d.cts → index-jcJwmBYt.d.cts} +500 -7
- package/dist/index.cjs +1603 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -27
- package/dist/index.d.ts +154 -27
- package/dist/index.js +1584 -26
- package/dist/index.js.map +1 -1
- package/dist/mention-text-CDnCFd3c.d.cts +53 -0
- package/dist/mention-text-CDnCFd3c.d.ts +53 -0
- package/dist/model-C1zApQM8.d.cts +219 -0
- package/dist/model-C1zApQM8.d.ts +219 -0
- package/dist/persistence/index.d.cts +3 -120
- package/dist/persistence/index.d.ts +3 -120
- package/dist/registry-CED5C0lA.d.cts +204 -0
- package/dist/registry-CED5C0lA.d.ts +204 -0
- package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
- package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
- package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
- package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
- package/dist/serializers/index.cjs +12 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +41 -13
- package/dist/serializers/index.d.ts +41 -13
- package/dist/serializers/index.js +12 -4
- package/dist/spreadsheet/index.cjs +157 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +534 -11
- package/dist/spreadsheet/index.d.ts +534 -11
- package/dist/spreadsheet/index.js +157 -3
- package/dist/storage-BpFfspHm.d.cts +120 -0
- package/dist/storage-BpFfspHm.d.ts +120 -0
- package/dist/tokens/index.cjs +115 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +212 -0
- package/dist/tokens/index.d.ts +212 -0
- package/dist/tokens/index.js +115 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
- package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
- package/package.json +15 -1
- package/dist/chunk-3CN2WD3X.js.map +0 -1
- package/dist/chunk-5S3Z4PPF.js.map +0 -1
- package/dist/chunk-D27YFC46.cjs.map +0 -1
- package/dist/chunk-FODNLX5N.js.map +0 -1
- package/dist/chunk-ITBSKI26.js.map +0 -1
- package/dist/chunk-MA2F5KHF.cjs +0 -3223
- package/dist/chunk-MA2F5KHF.cjs.map +0 -1
- package/dist/chunk-MCSJFWAO.cjs.map +0 -1
- package/dist/chunk-OLZJXL4R.cjs.map +0 -1
- package/dist/chunk-PGNZKFQS.js.map +0 -1
- package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
- package/dist/chunk-U5KWZU4X.js +0 -3223
- package/dist/chunk-U5KWZU4X.js.map +0 -1
- package/dist/chunk-XNORY7M6.cjs.map +0 -1
- package/dist/controller-BhLLAoi8.d.cts +0 -1000
- package/dist/controller-BhLLAoi8.d.ts +0 -1000
- package/dist/types-DX4Mh4SL.d.cts +0 -217
- package/dist/types-DX4Mh4SL.d.ts +0 -217
- /package/dist/{chunk-3LV6RRPH.js.map → chunk-MHEPLBFB.js.map} +0 -0
|
@@ -3,10 +3,11 @@ import {
|
|
|
3
3
|
DELETION_MARK_NAME,
|
|
4
4
|
FORMAT_CHANGE_MARK_NAME,
|
|
5
5
|
INSERTION_MARK_NAME,
|
|
6
|
+
MOVE_ID_ATTRIBUTE,
|
|
6
7
|
createReviewId,
|
|
7
8
|
getTrackedChanges,
|
|
8
9
|
resolveTrackedChanges
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SWE3NPOU.js";
|
|
10
11
|
|
|
11
12
|
// src/compare/diff.ts
|
|
12
13
|
var EQUAL = 0;
|
|
@@ -674,16 +675,48 @@ function compareChildren(original, revised, oPath, rPath, ctx) {
|
|
|
674
675
|
}
|
|
675
676
|
return out;
|
|
676
677
|
}
|
|
678
|
+
function detectMovedBlocks(children, ctx) {
|
|
679
|
+
const keyOf = (node) => `${node.type.name}\0${filteredAttributes(node.attrs, ctx)}\0${stableStringify(node.content.toJSON())}`;
|
|
680
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
681
|
+
children.forEach((node, index) => {
|
|
682
|
+
const operation = node.attrs?.[BLOCK_CHANGE_ATTRIBUTE]?.operation;
|
|
683
|
+
if (!operation || !node.isTextblock || !node.textContent.trim()) return;
|
|
684
|
+
if (!node.type.allowsMarkType(ctx.insertion) || !node.type.allowsMarkType(ctx.deletion)) return;
|
|
685
|
+
const key = keyOf(node);
|
|
686
|
+
const entry = candidates.get(key) ?? { del: [], ins: [] };
|
|
687
|
+
(operation === "delete" ? entry.del : entry.ins).push(index);
|
|
688
|
+
candidates.set(key, entry);
|
|
689
|
+
});
|
|
690
|
+
const out = [...children];
|
|
691
|
+
candidates.forEach(({ del, ins }) => {
|
|
692
|
+
if (del.length !== 1 || ins.length !== 1) return;
|
|
693
|
+
const attrs = newAttributes(ctx);
|
|
694
|
+
const moved = { ...attrs, [MOVE_ID_ATTRIBUTE]: attrs.changeId };
|
|
695
|
+
const relink = (index, markType) => {
|
|
696
|
+
const node = out[index];
|
|
697
|
+
out[index] = node.type.create(
|
|
698
|
+
{ ...node.attrs, [BLOCK_CHANGE_ATTRIBUTE]: null },
|
|
699
|
+
markInline(node.content, markType.create(moved)),
|
|
700
|
+
node.marks
|
|
701
|
+
);
|
|
702
|
+
};
|
|
703
|
+
relink(del[0], ctx.deletion);
|
|
704
|
+
relink(ins[0], ctx.insertion);
|
|
705
|
+
});
|
|
706
|
+
return out;
|
|
707
|
+
}
|
|
677
708
|
function summarize(doc, notes) {
|
|
678
709
|
const summary = {
|
|
679
710
|
insertions: 0,
|
|
680
711
|
deletions: 0,
|
|
681
712
|
formatChanges: 0,
|
|
682
713
|
blockChanges: 0,
|
|
714
|
+
moves: 0,
|
|
683
715
|
notHonoured: [...notes]
|
|
684
716
|
};
|
|
685
717
|
getTrackedChanges(doc).forEach((change) => {
|
|
686
|
-
if (change.type === "
|
|
718
|
+
if (change.type === "move") summary.moves++;
|
|
719
|
+
else if (change.type === "insertion" || change.block?.operation === "insert") summary.insertions++;
|
|
687
720
|
else if (change.type === "deletion" || change.block?.operation === "delete") summary.deletions++;
|
|
688
721
|
else if (change.type === "format") summary.formatChanges++;
|
|
689
722
|
else summary.blockChanges++;
|
|
@@ -726,7 +759,8 @@ function compareDocuments(original, revised, options = {}) {
|
|
|
726
759
|
};
|
|
727
760
|
const a = prepare(original);
|
|
728
761
|
const b = prepare(revised);
|
|
729
|
-
|
|
762
|
+
let children = compareChildren(a, b, [], [], ctx);
|
|
763
|
+
if (options.detectMoves) children = detectMovedBlocks(children, ctx);
|
|
730
764
|
let doc;
|
|
731
765
|
try {
|
|
732
766
|
doc = b.type.createChecked(b.attrs, children, b.marks);
|
|
@@ -746,4 +780,4 @@ export {
|
|
|
746
780
|
compareDocuments,
|
|
747
781
|
compareHtml
|
|
748
782
|
};
|
|
749
|
-
//# sourceMappingURL=chunk-
|
|
783
|
+
//# sourceMappingURL=chunk-QJSOZFCZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compare/diff.ts","../src/compare/documents.ts"],"sourcesContent":["// Portions ported from pdfcraft/crates/compare/src/lib.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/**\n * Generic sequence diff: common prefix/suffix stripping, Myers' O((N+M)·D)\n * algorithm for small segments and patience anchors (items unique on both\n * sides, longest increasing subsequence) to split long ones. Dependency-free.\n */\n\nexport type DiffOpType = \"equal\" | \"delete\" | \"insert\";\n\n/** A run of consecutive operations of one type. */\nexport interface DiffRun {\n type: DiffOpType;\n /** Start index in `a` (for `insert`, where the items go in `a`). */\n aIndex: number;\n /** Start index in `b` (for `delete`, where the items were in `b`). */\n bIndex: number;\n length: number;\n}\n\n/** A change: a deleted range of `a`, an inserted range of `b`, or both (replaced). */\nexport interface DiffChange {\n kind: \"inserted\" | \"deleted\" | \"replaced\";\n aStart: number;\n aEnd: number;\n bStart: number;\n bEnd: number;\n}\n\nexport interface DiffSequencesOptions<T> {\n /**\n * Key of an item: items with the same key are equal. Defaults to the item\n * itself for strings, numbers and booleans, and `JSON.stringify` otherwise.\n * Keys enable patience anchors for long inputs.\n */\n hash?: (item: T) => string | number | boolean;\n /**\n * Custom equality. Without `hash`, segments longer than `direct` are only\n * diffed while the edit distance stays below `direct`, otherwise replaced.\n */\n equals?: (a: T, b: T) => boolean;\n /**\n * Segments with more items (both sides together) are split at anchors\n * first so Myers' saved frontiers (O(D²) memory) stay small. Default 3000.\n */\n direct?: number;\n /** Maximum anchor recursion depth before a long segment is replaced whole. Default 8. */\n maxDepth?: number;\n}\n\nconst EQUAL = 0;\nconst DELETE = 1;\nconst INSERT = 2;\n\ntype Eq = (i: number, j: number) => boolean;\n\nfunction pushRepeat(out: number[], op: number, count: number): void {\n for (let i = 0; i < count; i++) out.push(op);\n}\n\n/**\n * Myers' diff of a[aLo..aHi) and b[bLo..bHi). Returns false (and emits\n * nothing) when the edit distance exceeds `maxD`.\n */\nfunction myers(\n aLo: number,\n aHi: number,\n bLo: number,\n bHi: number,\n eq: Eq,\n out: number[],\n maxD: number,\n): boolean {\n const n = aHi - aLo;\n const m = bHi - bLo;\n if (n === 0 || m === 0) {\n pushRepeat(out, DELETE, n);\n pushRepeat(out, INSERT, m);\n return true;\n }\n const max = n + m;\n const off = max + 1;\n const v = new Int32Array(2 * max + 3);\n // Frontier d only spans diagonals -d..d: save just that slice.\n const trace: Int32Array[] = [];\n const limit = Math.min(max, maxD);\n let found = false;\n outer: for (let d = 0; d <= limit; d++) {\n trace.push(v.slice(Math.max(off - d - 1, 0), off + d + 2));\n for (let k = -d; k <= d; k += 2) {\n const i = k + off;\n let x = k === -d || (k !== d && v[i - 1] < v[i + 1]) ? v[i + 1] : v[i - 1] + 1;\n let y = x - k;\n while (x < n && y < m && eq(aLo + x, bLo + y)) {\n x++;\n y++;\n }\n v[i] = x;\n if (x >= n && y >= m) {\n found = true;\n break outer;\n }\n }\n }\n if (!found) return false;\n\n // Walk back through the saved frontiers.\n const ops: number[] = [];\n let x = n;\n let y = m;\n for (let d = trace.length - 1; d >= 0; d--) {\n const frontier = trace[d];\n const base = Math.max(off - d - 1, 0);\n const at = (k: number) => frontier[k + off - base];\n const k = x - y;\n const prevK = k === -d || (k !== d && at(k - 1) < at(k + 1)) ? k + 1 : k - 1;\n const prevX = at(prevK);\n const prevY = prevX - prevK;\n while (x > prevX && y > prevY) {\n ops.push(EQUAL);\n x--;\n y--;\n }\n if (d > 0) ops.push(x === prevX ? INSERT : DELETE);\n x = prevX;\n y = prevY;\n }\n for (let i = ops.length - 1; i >= 0; i--) out.push(ops[i]);\n return true;\n}\n\n/**\n * Patience anchors: items that occur once on each side, in an order both\n * sides agree on (the longest increasing subsequence of their positions).\n */\nfunction anchors(\n ids: { a: Int32Array; b: Int32Array },\n aLo: number,\n aHi: number,\n bLo: number,\n bHi: number,\n): [number, number][] {\n const count = new Map<number, [number, number, number, number]>();\n for (let i = aLo; i < aHi; i++) {\n const id = ids.a[i];\n const entry = count.get(id);\n if (entry) {\n entry[0]++;\n } else {\n count.set(id, [1, 0, i, -1]);\n }\n }\n for (let j = bLo; j < bHi; j++) {\n const entry = count.get(ids.b[j]);\n if (entry) {\n entry[1]++;\n entry[3] = j;\n }\n }\n const pairs: [number, number][] = [];\n count.forEach((entry) => {\n if (entry[0] === 1 && entry[1] === 1) pairs.push([entry[2], entry[3]]);\n });\n pairs.sort((p, q) => p[0] - q[0]);\n // LIS on the b positions (patience sorting with back links).\n const tails: number[] = [];\n const prev = new Int32Array(pairs.length).fill(-1);\n for (let k = 0; k < pairs.length; k++) {\n const y = pairs[k][1];\n let lo = 0;\n let hi = tails.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (pairs[tails[mid]][1] < y) lo = mid + 1;\n else hi = mid;\n }\n if (lo > 0) prev[k] = tails[lo - 1];\n if (lo === tails.length) tails.push(k);\n else tails[lo] = k;\n }\n const result: [number, number][] = [];\n let k = tails.length ? tails[tails.length - 1] : -1;\n while (k !== -1) {\n result.push(pairs[k]);\n k = prev[k];\n }\n return result.reverse();\n}\n\ninterface DiffContext {\n eq: Eq;\n ids: { a: Int32Array; b: Int32Array } | null;\n direct: number;\n maxDepth: number;\n}\n\nfunction diffRange(\n ctx: DiffContext,\n aLo: number,\n aHi: number,\n bLo: number,\n bHi: number,\n out: number[],\n depth: number,\n): void {\n const { eq } = ctx;\n let prefix = 0;\n while (aLo + prefix < aHi && bLo + prefix < bHi && eq(aLo + prefix, bLo + prefix)) prefix++;\n let suffix = 0;\n while (\n aHi - suffix > aLo + prefix &&\n bHi - suffix > bLo + prefix &&\n eq(aHi - suffix - 1, bHi - suffix - 1)\n ) {\n suffix++;\n }\n pushRepeat(out, EQUAL, prefix);\n const ma = aLo + prefix;\n const mb = bLo + prefix;\n const ea = aHi - suffix;\n const eb = bHi - suffix;\n const replace = () => {\n pushRepeat(out, DELETE, ea - ma);\n pushRepeat(out, INSERT, eb - mb);\n };\n\n if (ea - ma + (eb - mb) <= ctx.direct) {\n myers(ma, ea, mb, eb, eq, out, Infinity);\n } else if (!ctx.ids) {\n // No keys, so no anchors: bounded Myers, else one replacement.\n if (!myers(ma, ea, mb, eb, eq, out, ctx.direct)) replace();\n } else {\n const found = depth >= ctx.maxDepth ? [] : anchors(ctx.ids, ma, ea, mb, eb);\n if (found.length === 0) {\n // A long stretch with nothing unique in common: one replacement.\n replace();\n } else {\n let i = ma;\n let j = mb;\n for (const [x, y] of found) {\n diffRange(ctx, i, x, j, y, out, depth + 1);\n out.push(EQUAL);\n i = x + 1;\n j = y + 1;\n }\n diffRange(ctx, i, ea, j, eb, out, depth + 1);\n }\n }\n pushRepeat(out, EQUAL, suffix);\n}\n\nconst defaultHash = (item: unknown): string | number | boolean =>\n typeof item === \"string\" || typeof item === \"number\" || typeof item === \"boolean\"\n ? item\n : JSON.stringify(item);\n\n/**\n * Diffs two sequences and returns the edit script as runs of equal, deleted\n * and inserted items, in order. Within a changed stretch deletions come\n * before insertions. The script is minimal for segments up to `direct`\n * items; longer inputs are split at patience anchors (bounded work).\n */\nexport function diffSequences<T>(\n a: readonly T[],\n b: readonly T[],\n options: DiffSequencesOptions<T> = {},\n): DiffRun[] {\n const direct = Math.max(2, options.direct ?? 3000);\n const maxDepth = options.maxDepth ?? 8;\n let ids: { a: Int32Array; b: Int32Array } | null = null;\n let eq: Eq;\n if (options.equals && !options.hash) {\n const equals = options.equals;\n eq = (i, j) => equals(a[i], b[j]);\n } else {\n const hash = options.hash ?? (defaultHash as (item: T) => string | number | boolean);\n const table = new Map<string | number | boolean, number>();\n const idOf = (item: T) => {\n const key = hash(item);\n let id = table.get(key);\n if (id === undefined) {\n id = table.size;\n table.set(key, id);\n }\n return id;\n };\n ids = { a: Int32Array.from(a, idOf), b: Int32Array.from(b, idOf) };\n const { a: ia, b: ib } = ids;\n eq = (i, j) => ia[i] === ib[j];\n }\n\n const ops: number[] = [];\n diffRange({ eq, ids, direct, maxDepth }, 0, a.length, 0, b.length, ops, 0);\n\n // Group into runs, deletions first within each changed stretch.\n const runs: DiffRun[] = [];\n const push = (type: DiffOpType, aIndex: number, bIndex: number, length: number) => {\n if (length === 0) return;\n const last = runs[runs.length - 1];\n if (last && last.type === type) {\n last.length += length;\n } else {\n runs.push({ type, aIndex, bIndex, length });\n }\n };\n let i = 0;\n let j = 0;\n let k = 0;\n while (k < ops.length) {\n if (ops[k] === EQUAL) {\n let n = 0;\n while (k < ops.length && ops[k] === EQUAL) {\n n++;\n k++;\n }\n push(\"equal\", i, j, n);\n i += n;\n j += n;\n continue;\n }\n let dels = 0;\n let inss = 0;\n while (k < ops.length && ops[k] !== EQUAL) {\n if (ops[k] === DELETE) dels++;\n else inss++;\n k++;\n }\n push(\"delete\", i, j, dels);\n push(\"insert\", i + dels, j, inss);\n i += dels;\n j += inss;\n }\n return runs;\n}\n\n/** Groups an edit script into inserted, deleted and replaced ranges. */\nexport function diffChanges<T>(\n a: readonly T[],\n b: readonly T[],\n options: DiffSequencesOptions<T> = {},\n): DiffChange[] {\n const changes: DiffChange[] = [];\n let current: DiffChange | null = null;\n for (const run of diffSequences(a, b, options)) {\n if (run.type === \"equal\") {\n current = null;\n continue;\n }\n if (!current) {\n current = {\n kind: run.type === \"delete\" ? \"deleted\" : \"inserted\",\n aStart: run.aIndex,\n aEnd: run.aIndex,\n bStart: run.bIndex,\n bEnd: run.bIndex,\n };\n changes.push(current);\n }\n if (run.type === \"delete\") {\n current.aEnd = run.aIndex + run.length;\n } else {\n current.bEnd = run.bIndex + run.length;\n }\n if (current.aEnd > current.aStart && current.bEnd > current.bStart) current.kind = \"replaced\";\n }\n return changes;\n}\n","// Portions ported from wordcraft/crates/engine/src/cmd/tools.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\nimport { createDocument } from \"@tiptap/core\";\nimport {\n Fragment,\n Mark as ProsemirrorMark,\n type MarkType,\n type Node as ProsemirrorNode,\n type Schema,\n} from \"@tiptap/pm/model\";\nimport {\n BLOCK_CHANGE_ATTRIBUTE,\n DELETION_MARK_NAME,\n FORMAT_CHANGE_MARK_NAME,\n INSERTION_MARK_NAME,\n MOVE_ID_ATTRIBUTE,\n getTrackedChanges,\n resolveTrackedChanges,\n type TrackedBlockChangeAttributes,\n type TrackedBlockOperation,\n type TrackedBlockSnapshot,\n type TrackedChangeAttributes,\n type TrackedMark,\n} from \"../extensions/track-changes\";\nimport { createReviewId, type ReviewAuthor } from \"../extensions/comment-store\";\nimport { diffSequences } from \"./diff\";\n\n/** A document to compare: a ProseMirror node, its JSON, or HTML (needs a DOM). */\nexport type CompareSource = ProsemirrorNode | Record<string, any> | string;\n\nexport interface CompareDocumentsOptions {\n /** Schema of the documents. Required unless both sources are ProseMirror nodes. */\n schema?: Schema;\n /** Author recorded on every change. Default `{ id: \"compare\", name: \"Compare\" }`. */\n author?: ReviewAuthor;\n /** Time recorded on every change (`createdAt`). Default now. */\n date?: number | Date | string;\n /** Compare changed paragraphs word by word (default) or character by character. */\n granularity?: \"word\" | \"char\";\n /** Treat text differing only in case as equal (the revised text is kept). */\n ignoreCase?: boolean;\n /** Treat runs of white space as equal (the revised text is kept). */\n ignoreWhitespace?: boolean;\n /** Record formatting differences of equal text as `formatChange` marks. Default `true`. */\n formatting?: boolean;\n /** Change id generator. */\n generateId?: () => string;\n /** Marks not compared as formatting (in addition to review marks, comments and bookmarks). */\n ignoredMarks?: string[];\n /** Node attributes ignored when comparing blocks. Default `[\"id\"]`. */\n ignoredBlockAttributes?: string[];\n /**\n * Paragraphs longer than this (in characters) are compared by words even\n * at `granularity: \"char\"`. Default 2000.\n */\n maxCharDiff?: number;\n /**\n * Record a top-level paragraph that was deleted in one place and inserted,\n * identical, elsewhere as a tracked move (`type: \"move\"`) instead of a\n * whole-block deletion and insertion. Only paragraphs whose content occurs\n * once among the deleted and once among the inserted blocks are paired.\n * Default `false`.\n */\n detectMoves?: boolean;\n}\n\nexport interface CompareSummary {\n /** Inserted text runs and inserted block runs. */\n insertions: number;\n /** Deleted text runs and deleted block runs. */\n deletions: number;\n /** Formatting changes of equal text. */\n formatChanges: number;\n /** Block type, attribute and wrapping changes. */\n blockChanges: number;\n /** Moved paragraphs (with `detectMoves`). */\n moves: number;\n /** Differences the result could not represent as tracked changes. */\n notHonoured: string[];\n}\n\nexport interface CompareDocumentsResult {\n /**\n * The revised document with the differences as tracked changes: accepting\n * all changes yields the revised document, rejecting all the original.\n */\n doc: ProsemirrorNode;\n summary: CompareSummary;\n}\n\n/** Marks that are never compared as formatting. */\nconst REVIEW_MARK_NAMES = new Set([\n INSERTION_MARK_NAME,\n DELETION_MARK_NAME,\n FORMAT_CHANGE_MARK_NAME,\n \"comment\",\n \"bookmark\",\n]);\n\n/** Container types that pair with each other (a list whose type changed). */\nconst LIST_TYPES = new Set([\"bulletList\", \"orderedList\"]);\n\nconst OBJECT_REPLACEMENT = \"\";\n\ntype Wrapper = TrackedBlockSnapshot[\"wrappers\"][number];\n\ninterface Unit {\n /** One code point, or \"\" for an inline leaf node. */\n text: string;\n node: ProsemirrorNode | null;\n marks: readonly ProsemirrorMark[];\n}\n\ninterface Context {\n schema: Schema;\n author: ReviewAuthor;\n createdAt: number;\n generateId: () => string;\n granularity: \"word\" | \"char\";\n ignoreCase: boolean;\n ignoreWhitespace: boolean;\n formatting: boolean;\n maxCharDiff: number;\n ignoredMarks: Set<string>;\n ignoredAttributes: Set<string>;\n insertion?: MarkType;\n deletion?: MarkType;\n format?: MarkType;\n notes: Set<string>;\n keys: Map<ProsemirrorNode, string>;\n /** Shared change of the textblocks inside a container whose type changed. */\n wrapperChange: (() => TrackedChangeAttributes) | null;\n}\n\nfunction stableStringify(value: unknown): string {\n if (value === undefined) return \"null\";\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const keys = Object.keys(value as object).sort();\n return `{${keys\n .map((key) => `${JSON.stringify(key)}:${stableStringify((value as any)[key])}`)\n .join(\",\")}}`;\n}\n\nfunction toTime(date: CompareDocumentsOptions[\"date\"]): number {\n if (date instanceof Date) return date.getTime();\n if (typeof date === \"string\") {\n const time = Date.parse(date);\n return Number.isNaN(time) ? Date.now() : time;\n }\n return typeof date === \"number\" ? date : Date.now();\n}\n\nfunction toNode(source: CompareSource, schema: Schema): ProsemirrorNode {\n if (typeof source === \"string\") return createDocument(source, schema);\n if (typeof (source as ProsemirrorNode).descendants === \"function\") {\n const node = source as ProsemirrorNode;\n return node.type.schema === schema ? node : schema.nodeFromJSON(node.toJSON());\n }\n return schema.nodeFromJSON(source);\n}\n\nfunction newAttributes(ctx: Context): TrackedChangeAttributes {\n return {\n changeId: ctx.generateId(),\n authorId: ctx.author.id,\n authorName: ctx.author.name,\n authorColor: ctx.author.color ?? null,\n createdAt: ctx.createdAt,\n };\n}\n\n/** Lazily created attributes shared by one run of changes. */\nfunction lazyAttributes(ctx: Context): () => TrackedChangeAttributes {\n let attrs: TrackedChangeAttributes | null = null;\n return () => (attrs = attrs ?? newAttributes(ctx));\n}\n\nfunction stripBlockChange(attrs: Record<string, any>): Record<string, any> {\n const copy = { ...attrs };\n delete copy[BLOCK_CHANGE_ATTRIBUTE];\n return copy;\n}\n\nfunction wrapperOf(node: ProsemirrorNode): Wrapper {\n return { type: node.type.name, attrs: stripBlockChange(node.attrs) };\n}\n\nfunction snapshot(node: ProsemirrorNode, wrappers: Wrapper[]): TrackedBlockSnapshot {\n return { type: node.type.name, attrs: stripBlockChange(node.attrs), wrappers };\n}\n\nfunction filteredAttributes(attrs: Record<string, any>, ctx: Context): string {\n return stableStringify(\n Object.fromEntries(\n Object.entries(attrs ?? {}).filter(\n ([key]) => key !== BLOCK_CHANGE_ATTRIBUTE && !ctx.ignoredAttributes.has(key),\n ),\n ),\n );\n}\n\n/** Mirrors the tracker's block comparison: type, attributes and wrapper types. */\nfunction sameBlock(a: TrackedBlockSnapshot, b: TrackedBlockSnapshot, ctx: Context): boolean {\n if (a.type !== b.type) return false;\n if (filteredAttributes(a.attrs, ctx) !== filteredAttributes(b.attrs, ctx)) return false;\n if (a.wrappers.length !== b.wrappers.length) return false;\n return a.wrappers.every((w, i) => w.type === b.wrappers[i].type);\n}\n\nconst hasBlockChange = (node: ProsemirrorNode) =>\n !!node.type.spec.attrs && BLOCK_CHANGE_ATTRIBUTE in node.type.spec.attrs;\n\nconst label = (node: ProsemirrorNode) => node.type.name;\n\n// ---------------------------------------------------------------------------\n// Keys\n// ---------------------------------------------------------------------------\n\nfunction normalize(text: string, ctx: Context): string {\n let result = ctx.ignoreWhitespace ? text.replace(/\\s+/g, \" \") : text;\n if (ctx.ignoreCase) result = result.toLowerCase();\n return result;\n}\n\nfunction leafKey(node: ProsemirrorNode): string {\n return `${node.type.name}${stableStringify(stripBlockChange(node.attrs))}`;\n}\n\nfunction group(node: ProsemirrorNode): string {\n return LIST_TYPES.has(node.type.name) ? \"list\" : node.type.name;\n}\n\n/** Block key: textblocks by their (normalized) text only, so type changes pair up. */\nfunction blockKey(node: ProsemirrorNode, ctx: Context): string {\n const cached = ctx.keys.get(node);\n if (cached !== undefined) return cached;\n let key: string;\n if (node.isTextblock) {\n let text = \"\";\n node.forEach((child) => {\n text += child.isText ? child.text : `${OBJECT_REPLACEMENT}${leafKey(child)}${OBJECT_REPLACEMENT}`;\n });\n text = normalize(text, ctx);\n key = `T\\u0000${ctx.ignoreWhitespace ? text.trim() : text}`;\n } else if (node.isLeaf) {\n key = `A\\u0000${leafKey(node)}`;\n } else {\n const parts: string[] = [];\n node.forEach((child) => parts.push(blockKey(child, ctx)));\n key = `C\\u0000${group(node)}\\u0000${parts.join(\"\\u0001\")}\\u0002`;\n }\n ctx.keys.set(node, key);\n return key;\n}\n\nfunction compatible(a: ProsemirrorNode, b: ProsemirrorNode): boolean {\n if (a.isTextblock || b.isTextblock) return a.isTextblock && b.isTextblock;\n if (a.isLeaf || b.isLeaf) return a.isLeaf && b.isLeaf && a.type === b.type;\n return group(a) === group(b);\n}\n\n// ---------------------------------------------------------------------------\n// Whole blocks\n// ---------------------------------------------------------------------------\n\nfunction markInline(\n content: Fragment,\n mark: ProsemirrorMark,\n): Fragment {\n const nodes: ProsemirrorNode[] = [];\n content.forEach((child) => nodes.push(child.mark(mark.addToSet(child.marks))));\n return Fragment.from(nodes);\n}\n\n/**\n * A block deleted or inserted as a whole: every textblock inside gets a\n * `blockChange` record with the operation (one change id per run).\n */\nfunction wholeBlock(\n node: ProsemirrorNode,\n operation: TrackedBlockOperation,\n wrappers: Wrapper[],\n attrs: () => TrackedChangeAttributes,\n ctx: Context,\n): ProsemirrorNode[] {\n const verb = operation === \"insert\" ? \"Inserted\" : \"Deleted\";\n if (node.isTextblock) {\n if (hasBlockChange(node)) {\n const record: TrackedBlockChangeAttributes = {\n ...attrs(),\n previous: snapshot(node, wrappers),\n operation,\n };\n return [node.type.create({ ...node.attrs, [BLOCK_CHANGE_ATTRIBUTE]: record }, node.content, node.marks)];\n }\n // No block record: fall back to marking the text.\n const markType = operation === \"insert\" ? ctx.insertion : ctx.deletion;\n if (markType && node.type.allowsMarkType(markType) && node.content.size > 0) {\n ctx.notes.add(\n `${verb} ${label(node)} blocks have no \"${BLOCK_CHANGE_ATTRIBUTE}\" attribute: only their text is tracked, so an empty block remains when the change is ${operation === \"insert\" ? \"rejected\" : \"accepted\"}.`,\n );\n return [node.type.create(node.attrs, markInline(node.content, markType.create(attrs())), node.marks)];\n }\n ctx.notes.add(`${verb} ${label(node)} blocks cannot be tracked.`);\n return operation === \"insert\" ? [node] : [];\n }\n if (node.isLeaf) {\n ctx.notes.add(`${verb} ${label(node)} blocks cannot be tracked.`);\n return operation === \"insert\" ? [node] : [];\n }\n const inner = [...wrappers, wrapperOf(node)];\n const children: ProsemirrorNode[] = [];\n node.forEach((child) => children.push(...wholeBlock(child, operation, inner, attrs, ctx)));\n try {\n return [node.type.createChecked(node.attrs, children, node.marks)];\n } catch {\n ctx.notes.add(`${verb} ${label(node)} blocks cannot be tracked.`);\n return operation === \"insert\" ? [node] : [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Inline comparison\n// ---------------------------------------------------------------------------\n\nfunction units(node: ProsemirrorNode): Unit[] {\n const result: Unit[] = [];\n node.forEach((child) => {\n if (child.isText) {\n for (const ch of child.text ?? \"\") result.push({ text: ch, node: null, marks: child.marks });\n } else {\n result.push({ text: \"\", node: child, marks: child.marks });\n }\n });\n return result;\n}\n\nconst SPACE = /\\s/u;\nconst WORD = /[\\p{L}\\p{N}\\p{M}_]/u;\n\nconst isSpace = (u: Unit) => !u.node && SPACE.test(u.text);\nconst isWord = (u: Unit) => !u.node && WORD.test(u.text);\n\n/** Token ranges: words, white space runs and single symbols, or characters. */\nfunction tokenize(list: Unit[], chars: boolean): [number, number][] {\n const tokens: [number, number][] = [];\n if (chars) {\n for (let i = 0; i < list.length; i++) tokens.push([i, i + 1]);\n return tokens;\n }\n let i = 0;\n while (i < list.length) {\n const start = i;\n const unit = list[i];\n if (isSpace(unit)) {\n while (i < list.length && isSpace(list[i])) i++;\n } else if (isWord(unit)) {\n while (i < list.length && isWord(list[i])) i++;\n } else {\n i++;\n }\n tokens.push([start, i]);\n }\n return tokens;\n}\n\ntype Stretch =\n | { type: \"equal\"; aIndex: number; bIndex: number; length: number }\n | { type: \"change\"; aFrom: number; aTo: number; bFrom: number; bTo: number };\n\n/**\n * Groups token runs into equal stretches and changes. White space alone\n * between two changes joins them, so \"a b\" → \"x y\" reads as one\n * replacement instead of alternating deletions and insertions.\n */\nfunction stretches(runs: ReturnType<typeof diffSequences>, list: Unit[], tokens: [number, number][]): Stretch[] {\n const spaceOnly = (bIndex: number, length: number) => {\n for (let t = bIndex; t < bIndex + length; t++) {\n const [from, to] = tokens[t];\n for (let k = from; k < to; k++) if (!isSpace(list[k])) return false;\n }\n return true;\n };\n const result: Stretch[] = [];\n runs.forEach((run, index) => {\n const prev = runs[index - 1];\n const next = runs[index + 1];\n const absorbed =\n run.type === \"equal\" &&\n prev &&\n next &&\n prev.type !== \"equal\" &&\n next.type !== \"equal\" &&\n spaceOnly(run.bIndex, run.length);\n if (run.type === \"equal\" && !absorbed) {\n result.push({ type: \"equal\", aIndex: run.aIndex, bIndex: run.bIndex, length: run.length });\n return;\n }\n let last = result[result.length - 1];\n if (!last || last.type !== \"change\") {\n last = { type: \"change\", aFrom: run.aIndex, aTo: run.aIndex, bFrom: run.bIndex, bTo: run.bIndex };\n result.push(last);\n }\n if (run.type !== \"insert\") last.aTo = run.aIndex + run.length;\n if (run.type !== \"delete\") last.bTo = run.bIndex + run.length;\n });\n return result;\n}\n\nfunction tokenKey(list: Unit[], [from, to]: [number, number], ctx: Context): string {\n let text = \"\";\n for (let i = from; i < to; i++) {\n const unit = list[i];\n text += unit.node ? `${OBJECT_REPLACEMENT}${leafKey(unit.node)}${OBJECT_REPLACEMENT}` : unit.text;\n }\n return normalize(text, ctx);\n}\n\nfunction formattingOf(marks: readonly ProsemirrorMark[], ctx: Context): ProsemirrorMark[] {\n return marks.filter(\n (mark) => !REVIEW_MARK_NAMES.has(mark.type.name) && !ctx.ignoredMarks.has(mark.type.name),\n );\n}\n\nfunction withoutReviewMarks(marks: readonly ProsemirrorMark[]): readonly ProsemirrorMark[] {\n return marks.filter(\n (mark) =>\n mark.type.name !== INSERTION_MARK_NAME &&\n mark.type.name !== DELETION_MARK_NAME &&\n mark.type.name !== FORMAT_CHANGE_MARK_NAME,\n );\n}\n\n/**\n * Merges the inline content of two paired textblocks: equal tokens keep the\n * revised text (with `formatChange` where formatting differs), deleted\n * tokens are re-inserted with the `deletion` mark before inserted tokens.\n */\nfunction mergeInline(\n original: ProsemirrorNode,\n revised: ProsemirrorNode,\n parentType: ProsemirrorNode[\"type\"],\n ctx: Context,\n): Fragment {\n const a = units(original);\n const b = units(revised);\n let chars = ctx.granularity === \"char\";\n if (chars && Math.max(a.length, b.length) > ctx.maxCharDiff) {\n chars = false;\n ctx.notes.add(\n `Paragraphs longer than ${ctx.maxCharDiff} characters were compared by words instead of characters.`,\n );\n }\n const ta = tokenize(a, chars);\n const tb = tokenize(b, chars);\n const runs = diffSequences(\n ta.map((t) => tokenKey(a, t, ctx)),\n tb.map((t) => tokenKey(b, t, ctx)),\n );\n\n const format = ctx.formatting && ctx.format && parentType.allowsMarkType(ctx.format) ? ctx.format : null;\n const out: Unit[] = [];\n let formatRun: { attrs: TrackedChangeAttributes; key: string } | null = null;\n\n const emit = (\n source: Unit[],\n tokens: [number, number][],\n from: number,\n to: number,\n markType: MarkType,\n ) => {\n if (to <= from) return;\n const mark = markType.create(newAttributes(ctx));\n for (let k = tokens[from][0]; k < tokens[to - 1][1]; k++) {\n const unit = source[k];\n out.push({ ...unit, marks: mark.addToSet(withoutReviewMarks(unit.marks)) });\n }\n };\n\n for (const run of stretches(runs, b, tb)) {\n if (run.type === \"equal\") {\n for (let t = 0; t < run.length; t++) {\n const [aFrom, aTo] = ta[run.aIndex + t];\n const [bFrom, bTo] = tb[run.bIndex + t];\n for (let k = bFrom; k < bTo; k++) {\n const unit = b[k];\n const before = a[Math.min(aFrom + (k - bFrom), aTo - 1)];\n const marks = withoutReviewMarks(unit.marks);\n if (!format) {\n formatRun = null;\n out.push({ ...unit, marks });\n continue;\n }\n const oldFormat = formattingOf(before.marks, ctx);\n const newFormat = formattingOf(unit.marks, ctx);\n if (ProsemirrorMark.sameSet(oldFormat, newFormat)) {\n formatRun = null;\n out.push({ ...unit, marks });\n continue;\n }\n const beforeJSON = oldFormat.map((m) => m.toJSON() as TrackedMark);\n const afterJSON = newFormat.map((m) => m.toJSON() as TrackedMark);\n const key = stableStringify([beforeJSON, afterJSON]);\n if (!formatRun || formatRun.key !== key) formatRun = { attrs: newAttributes(ctx), key };\n const mark = format.create({ ...formatRun.attrs, before: beforeJSON, after: afterJSON });\n out.push({ ...unit, marks: mark.addToSet(marks) });\n }\n }\n continue;\n }\n formatRun = null;\n emit(a, ta, run.aFrom, run.aTo, ctx.deletion!);\n emit(b, tb, run.bFrom, run.bTo, ctx.insertion!);\n }\n\n const nodes: ProsemirrorNode[] = [];\n let i = 0;\n while (i < out.length) {\n const unit = out[i];\n if (unit.node) {\n nodes.push(unit.node.mark(unit.marks));\n i++;\n continue;\n }\n let text = unit.text;\n let j = i + 1;\n while (j < out.length && !out[j].node && ProsemirrorMark.sameSet(out[j].marks, unit.marks)) {\n text += out[j].text;\n j++;\n }\n nodes.push(ctx.schema.text(text, unit.marks));\n i = j;\n }\n return Fragment.from(nodes);\n}\n\n// ---------------------------------------------------------------------------\n// Blocks\n// ---------------------------------------------------------------------------\n\nfunction mergeTextblocks(\n original: ProsemirrorNode,\n revised: ProsemirrorNode,\n oPath: Wrapper[],\n rPath: Wrapper[],\n ctx: Context,\n): ProsemirrorNode[] {\n const replaceWhole = () => [\n ...wholeBlock(original, \"delete\", rPath, lazyAttributes(ctx), ctx),\n ...wholeBlock(revised, \"insert\", rPath, lazyAttributes(ctx), ctx),\n ];\n\n let attrs: Record<string, any> = { ...revised.attrs };\n const previous = snapshot(original, oPath);\n if (!sameBlock(previous, snapshot(revised, rPath), ctx)) {\n if (hasBlockChange(revised)) {\n // Only the wrappers changed (e.g. bulleted → numbered list): one change per container.\n const onlyWrappers =\n ctx.wrapperChange &&\n sameBlock(previous, { ...snapshot(revised, rPath), wrappers: previous.wrappers }, ctx);\n const base = onlyWrappers ? ctx.wrapperChange!() : newAttributes(ctx);\n attrs = { ...attrs, [BLOCK_CHANGE_ATTRIBUTE]: { ...base, previous } };\n } else {\n ctx.notes.add(`Block changes to ${label(revised)} blocks cannot be tracked.`);\n }\n } else if (BLOCK_CHANGE_ATTRIBUTE in attrs) {\n attrs[BLOCK_CHANGE_ATTRIBUTE] = null;\n }\n\n const type = revised.type;\n const inlineTracking =\n !!ctx.insertion &&\n !!ctx.deletion &&\n type.allowsMarkType(ctx.insertion) &&\n type.allowsMarkType(ctx.deletion);\n if (!inlineTracking) {\n // e.g. code blocks in a headless schema: equal text keeps the block,\n // anything else is replaced whole.\n if (!original.content.eq(revised.content)) return replaceWhole();\n return [type.create(attrs, revised.content, revised.marks)];\n }\n const content = mergeInline(original, revised, type, ctx);\n if (!type.validContent(content)) {\n ctx.notes.add(`Changes between ${label(original)} and ${label(revised)} blocks were tracked as whole-block replacements.`);\n return replaceWhole();\n }\n return [type.create(attrs, content, revised.marks)];\n}\n\nfunction mergePair(\n original: ProsemirrorNode,\n revised: ProsemirrorNode,\n oPath: Wrapper[],\n rPath: Wrapper[],\n ctx: Context,\n): ProsemirrorNode[] {\n if (original.isTextblock) return mergeTextblocks(original, revised, oPath, rPath, ctx);\n const replaceWhole = () => [\n ...wholeBlock(original, \"delete\", rPath, lazyAttributes(ctx), ctx),\n ...wholeBlock(revised, \"insert\", rPath, lazyAttributes(ctx), ctx),\n ];\n if (original.isLeaf) {\n return original.eq(revised) ? [revised] : replaceWhole();\n }\n if (\n original.type === revised.type &&\n filteredAttributes(original.attrs, ctx) !== filteredAttributes(revised.attrs, ctx)\n ) {\n ctx.notes.add(`Attribute changes of ${label(revised)} nodes are not tracked.`);\n }\n const outer = ctx.wrapperChange;\n if (original.type !== revised.type) ctx.wrapperChange = lazyAttributes(ctx);\n let children: ProsemirrorNode[];\n try {\n children = compareChildren(\n original,\n revised,\n [...oPath, wrapperOf(original)],\n [...rPath, wrapperOf(revised)],\n ctx,\n );\n } finally {\n ctx.wrapperChange = outer;\n }\n try {\n return [revised.type.createChecked(revised.attrs, children, revised.marks)];\n } catch {\n ctx.notes.add(`Changes inside ${label(revised)} nodes were tracked as whole-block replacements.`);\n return replaceWhole();\n }\n}\n\nfunction compareChildren(\n original: ProsemirrorNode,\n revised: ProsemirrorNode,\n oPath: Wrapper[],\n rPath: Wrapper[],\n ctx: Context,\n): ProsemirrorNode[] {\n const a: ProsemirrorNode[] = [];\n const b: ProsemirrorNode[] = [];\n original.forEach((child) => a.push(child));\n revised.forEach((child) => b.push(child));\n const runs = diffSequences(a, b, { hash: (node) => blockKey(node, ctx) });\n const out: ProsemirrorNode[] = [];\n\n for (let r = 0; r < runs.length; r++) {\n const run = runs[r];\n if (run.type === \"equal\") {\n for (let t = 0; t < run.length; t++) {\n out.push(...mergePair(a[run.aIndex + t], b[run.bIndex + t], oPath, rPath, ctx));\n }\n continue;\n }\n // A run of deletions and insertions: the i-th deleted block pairs with\n // the i-th inserted one when they are alike; the rest are whole.\n let dels: ProsemirrorNode[] = [];\n let inss: ProsemirrorNode[] = [];\n if (run.type === \"delete\") {\n dels = a.slice(run.aIndex, run.aIndex + run.length);\n const next = runs[r + 1];\n if (next?.type === \"insert\") {\n inss = b.slice(next.bIndex, next.bIndex + next.length);\n r++;\n }\n } else {\n inss = b.slice(run.bIndex, run.bIndex + run.length);\n }\n const deleted = lazyAttributes(ctx);\n const inserted = lazyAttributes(ctx);\n for (let x = 0; x < Math.max(dels.length, inss.length); x++) {\n const del = dels[x];\n const ins = inss[x];\n if (del && ins && compatible(del, ins)) {\n out.push(...mergePair(del, ins, oPath, rPath, ctx));\n continue;\n }\n if (del) out.push(...wholeBlock(del, \"delete\", rPath, deleted, ctx));\n if (ins) out.push(...wholeBlock(ins, \"insert\", rPath, inserted, ctx));\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Moves\n// ---------------------------------------------------------------------------\n\n/**\n * Pairs top-level paragraphs deleted as a whole with identical paragraphs\n * inserted as a whole elsewhere, and records each pair as a move: the\n * source's text gets the deletion mark and the destination's the insertion\n * mark, both with the same move id (accepting or rejecting removes the\n * emptied block). Ambiguous content (several identical candidates) is left\n * as whole-block changes.\n */\nfunction detectMovedBlocks(children: ProsemirrorNode[], ctx: Context): ProsemirrorNode[] {\n const keyOf = (node: ProsemirrorNode) =>\n `${node.type.name}\\u0000${filteredAttributes(node.attrs, ctx)}\\u0000${stableStringify(node.content.toJSON())}`;\n const candidates = new Map<string, { del: number[]; ins: number[] }>();\n children.forEach((node, index) => {\n const operation = (node.attrs?.[BLOCK_CHANGE_ATTRIBUTE] as TrackedBlockChangeAttributes | null)\n ?.operation;\n if (!operation || !node.isTextblock || !node.textContent.trim()) return;\n if (!node.type.allowsMarkType(ctx.insertion!) || !node.type.allowsMarkType(ctx.deletion!)) return;\n const key = keyOf(node);\n const entry = candidates.get(key) ?? { del: [], ins: [] };\n (operation === \"delete\" ? entry.del : entry.ins).push(index);\n candidates.set(key, entry);\n });\n const out = [...children];\n candidates.forEach(({ del, ins }) => {\n if (del.length !== 1 || ins.length !== 1) return;\n const attrs = newAttributes(ctx);\n const moved = { ...attrs, [MOVE_ID_ATTRIBUTE]: attrs.changeId };\n const relink = (index: number, markType: MarkType) => {\n const node = out[index];\n out[index] = node.type.create(\n { ...node.attrs, [BLOCK_CHANGE_ATTRIBUTE]: null },\n markInline(node.content, markType.create(moved)),\n node.marks,\n );\n };\n relink(del[0], ctx.deletion!);\n relink(ins[0], ctx.insertion!);\n });\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nfunction summarize(doc: ProsemirrorNode, notes: Set<string>): CompareSummary {\n const summary: CompareSummary = {\n insertions: 0,\n deletions: 0,\n formatChanges: 0,\n blockChanges: 0,\n moves: 0,\n notHonoured: [...notes],\n };\n getTrackedChanges(doc).forEach((change) => {\n if (change.type === \"move\") summary.moves++;\n else if (change.type === \"insertion\" || change.block?.operation === \"insert\") summary.insertions++;\n else if (change.type === \"deletion\" || change.block?.operation === \"delete\") summary.deletions++;\n else if (change.type === \"format\") summary.formatChanges++;\n else summary.blockChanges++;\n });\n return summary;\n}\n\n/**\n * Compares two documents and returns the revised document with the\n * differences as tracked changes (`insertion`/`deletion`/`formatChange`\n * marks and `blockChange` records), plus a summary.\n *\n * Blocks are matched by their text (Myers diff with patience anchors); in a\n * run of changed blocks the i-th deleted block is paired with the i-th\n * inserted one and compared word by word (or by character), while unpaired\n * blocks are recorded as whole-block insertions/deletions. Equal text is\n * compared for formatting, and type/attribute/wrapping changes become\n * `blockChange` records. Without ignore options, accepting all changes\n * yields the revised document and rejecting all yields the original.\n *\n * The schema needs the `TrackChanges` extension (marks and the\n * `blockChange` attribute). Existing tracked changes in either document are\n * accepted first.\n */\nexport function compareDocuments(\n original: CompareSource,\n revised: CompareSource,\n options: CompareDocumentsOptions = {},\n): CompareDocumentsResult {\n const schema =\n options.schema ??\n (typeof (original as ProsemirrorNode)?.descendants === \"function\"\n ? (original as ProsemirrorNode).type.schema\n : typeof (revised as ProsemirrorNode)?.descendants === \"function\"\n ? (revised as ProsemirrorNode).type.schema\n : null);\n if (!schema) throw new Error(\"compareDocuments: `schema` is required for JSON or HTML sources.\");\n\n const ctx: Context = {\n schema,\n author: options.author ?? { id: \"compare\", name: \"Compare\" },\n createdAt: toTime(options.date),\n generateId: options.generateId ?? (() => createReviewId(\"change\")),\n granularity: options.granularity ?? \"word\",\n ignoreCase: !!options.ignoreCase,\n ignoreWhitespace: !!options.ignoreWhitespace,\n formatting: options.formatting !== false,\n maxCharDiff: options.maxCharDiff ?? 2000,\n ignoredMarks: new Set(options.ignoredMarks ?? []),\n ignoredAttributes: new Set(options.ignoredBlockAttributes ?? [\"id\"]),\n insertion: schema.marks[INSERTION_MARK_NAME],\n deletion: schema.marks[DELETION_MARK_NAME],\n format: schema.marks[FORMAT_CHANGE_MARK_NAME],\n notes: new Set(),\n keys: new Map(),\n wrapperChange: null,\n };\n if (!ctx.insertion || !ctx.deletion) {\n throw new Error(\"compareDocuments: the schema needs the TrackChanges extension (insertion/deletion marks).\");\n }\n if (ctx.formatting && !ctx.format) {\n ctx.notes.add(\"Formatting differences cannot be tracked: the schema has no formatChange mark.\");\n }\n\n const prepare = (source: CompareSource) => {\n const doc = toNode(source, schema);\n if (getTrackedChanges(doc).length === 0) return doc;\n ctx.notes.add(\"Tracked changes already in the documents were accepted before comparing.\");\n return resolveTrackedChanges(doc, \"accepted\");\n };\n const a = prepare(original);\n const b = prepare(revised);\n\n let children = compareChildren(a, b, [], [], ctx);\n if (options.detectMoves) children = detectMovedBlocks(children, ctx);\n let doc: ProsemirrorNode;\n try {\n doc = b.type.createChecked(b.attrs, children, b.marks);\n } catch {\n doc = b.type.createAndFill(b.attrs, children, b.marks) ?? b;\n ctx.notes.add(\"The compared document had to be repaired to fit the schema.\");\n }\n return { doc, summary: summarize(doc, ctx.notes) };\n}\n\n/** `compareDocuments` for two HTML strings (needs a DOM, like `setContent`). */\nexport function compareHtml(\n originalHtml: string,\n revisedHtml: string,\n options: CompareDocumentsOptions & { schema: Schema },\n): CompareDocumentsResult {\n return compareDocuments(originalHtml, revisedHtml, options);\n}\n"],"mappings":"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","names":["x","y","k","i","j","i","doc"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkNFVBT3UZcjs = require('./chunk-NFVBT3UZ.cjs');
|
|
5
5
|
|
|
6
6
|
// src/serializers/html.ts
|
|
7
7
|
var DROP_WITH_CONTENT_REGEX = /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
|
|
@@ -15,12 +15,12 @@ function sanitizeImportedHtml(html) {
|
|
|
15
15
|
const body = BODY_REGEX.exec(content);
|
|
16
16
|
if (body) content = body[1];
|
|
17
17
|
content = content.replace(DROP_WITH_CONTENT_REGEX, "").replace(UNCLOSED_RAW_TEXT_REGEX, "").replace(NAMESPACED_TAG_REGEX, "");
|
|
18
|
-
const sanitized =
|
|
18
|
+
const sanitized = _chunkNFVBT3UZcjs.sanitizeHtml.call(void 0, content, { disallowedTags: "strip" }).trim();
|
|
19
19
|
return sanitized || "<p></p>";
|
|
20
20
|
}
|
|
21
21
|
function plainTextToParagraphHtml(text) {
|
|
22
22
|
if (!text) return "<p></p>";
|
|
23
|
-
return text.replace(/\r\n?/g, "\n").split("\n").map((line) => `<p>${
|
|
23
|
+
return text.replace(/\r\n?/g, "\n").split("\n").map((line) => `<p>${_chunkNFVBT3UZcjs.escapeHtml.call(void 0, line)}</p>`).join("");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// src/serializers/rtf.ts
|
|
@@ -296,4 +296,4 @@ function readRichClipboard(event, schema, mimeType = PIXERATE_DOCUMENT_MIME) {
|
|
|
296
296
|
|
|
297
297
|
|
|
298
298
|
exports.sanitizeImportedHtml = sanitizeImportedHtml; exports.plainTextToParagraphHtml = plainTextToParagraphHtml; exports.rtfToPlainText = rtfToPlainText; exports.PIXERATE_DOCUMENT_MIME = PIXERATE_DOCUMENT_MIME; exports.getSchemaSignature = getSchemaSignature; exports.serializeRichClipboardPayload = serializeRichClipboardPayload; exports.parseRichClipboardPayload = parseRichClipboardPayload; exports.getClipboardData = getClipboardData; exports.readRichClipboard = readRichClipboard;
|
|
299
|
-
//# sourceMappingURL=chunk-
|
|
299
|
+
//# sourceMappingURL=chunk-RRREAOVM.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-SML64VJG.cjs","../src/serializers/html.ts","../src/serializers/rtf.ts","../src/serializers/clipboard.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-SML64VJG.cjs","sourcesContent":[null,"import { sanitizeHtml } from \"./markdown\";\nimport { escapeHtml } from \"./plain-text\";\n\n/**\n * Elements whose content is never document text (scripts, styles, metadata,\n * embedded objects, form controls). They are removed together with their\n * content before sanitizing, so e.g. CSS rules don't show up as paragraphs.\n */\nconst DROP_WITH_CONTENT_REGEX =\n /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\n/** An unclosed `<script>` / `<style>` swallows the rest of the document. */\nconst UNCLOSED_RAW_TEXT_REGEX = /<(script|style)\\b[^>]*>[\\s\\S]*$/i;\nconst BODY_REGEX = /<body\\b[^>]*>([\\s\\S]*?)(?:<\\/body\\s*>|$)/i;\n/** Namespaced tags such as Word's `<o:p>`, which the sanitizer cannot parse. */\nconst NAMESPACED_TAG_REGEX = /<\\/?[a-zA-Z][\\w-]*:[\\w-]+\\b[^>]*>/g;\n\n/**\n * Sanitizes a full HTML document or fragment for insertion into the editor,\n * e.g. an imported `.html` file or HTML from another application.\n *\n * - Keeps only the `<body>` content when the input is a full document.\n * - Removes comments, doctype/XML declarations, CDATA, scripts, styles and\n * other non-text elements together with their content.\n * - Applies the same tag/attribute/URL allowlist as embedded HTML in markdown\n * (see `sanitizeHtml`), but strips disallowed tags instead of escaping them,\n * so wrappers such as `<section>` or `<font>` keep their text.\n *\n * It is string-based (no live DOM), so handlers such as `<img onerror>` never run.\n */\nexport function sanitizeImportedHtml(html: string): string {\n if (!html) return \"<p></p>\";\n let content = html.replace(/^/, \"\");\n content = content\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, \"\")\n .replace(/<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>|$)/g, \"\")\n .replace(/<![^>]*>/g, \"\")\n .replace(/<\\?[\\s\\S]*?(?:\\?>|$)/g, \"\");\n const body = BODY_REGEX.exec(content);\n if (body) content = body[1];\n content = content\n .replace(DROP_WITH_CONTENT_REGEX, \"\")\n .replace(UNCLOSED_RAW_TEXT_REGEX, \"\")\n .replace(NAMESPACED_TAG_REGEX, \"\");\n const sanitized = sanitizeHtml(content, { disallowedTags: \"strip\" }).trim();\n return sanitized || \"<p></p>\";\n}\n\n/**\n * Converts untrusted plain text to TipTap paragraph HTML, always escaping it.\n *\n * Unlike `plainTextToTipTapHtml`, text that already looks like `<p>…</p>` is\n * not passed through as HTML, which matters for imported files.\n */\nexport function plainTextToParagraphHtml(text: string): string {\n if (!text) return \"<p></p>\";\n return text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => `<p>${escapeHtml(line)}</p>`)\n .join(\"\");\n}\n","/**\n * Destinations whose content is metadata or binary data rather than document\n * text. A group starting with one of these control words (or with `\\*`) is\n * skipped entirely.\n */\nconst SKIPPED_DESTINATIONS = new Set([\n \"author\", \"bkmkend\", \"bkmkstart\", \"colortbl\", \"comment\", \"datastore\",\n \"doccomm\", \"falt\", \"fldinst\", \"filetbl\", \"fonttbl\", \"footer\", \"footerf\",\n \"footerl\", \"footerr\", \"footnote\", \"generator\", \"header\", \"headerf\",\n \"headerl\", \"headerr\", \"info\", \"keywords\", \"latentstyles\", \"listoverridetable\",\n \"listtable\", \"nonshppict\", \"object\", \"operator\", \"pict\", \"private\", \"revtbl\",\n \"rsidtbl\", \"shppict\", \"stylesheet\", \"subject\", \"themedata\", \"title\",\n \"xmlnstbl\", \"colorschememapping\",\n]);\n\n/** Control words that map to a literal character. */\nconst CONTROL_WORD_TEXT: Record<string, string> = {\n par: \"\\n\",\n sect: \"\\n\",\n page: \"\\n\",\n line: \"\\n\",\n row: \"\\n\",\n cell: \"\\t\",\n tab: \"\\t\",\n emdash: \"—\",\n endash: \"–\",\n emspace: \" \",\n enspace: \" \",\n bullet: \"•\",\n lquote: \"‘\",\n rquote: \"’\",\n ldblquote: \"“\",\n rdblquote: \"”\",\n};\n\n/** Windows-1252 code points in 0x80–0x9F that differ from Latin-1. */\nconst CP1252: Record<number, string> = {\n 0x80: \"€\", 0x82: \"‚\", 0x83: \"ƒ\", 0x84: \"„\", 0x85: \"…\",\n 0x86: \"†\", 0x87: \"‡\", 0x88: \"ˆ\", 0x89: \"‰\", 0x8a: \"Š\",\n 0x8b: \"‹\", 0x8c: \"Œ\", 0x8e: \"Ž\", 0x91: \"‘\", 0x92: \"’\",\n 0x93: \"“\", 0x94: \"”\", 0x95: \"•\", 0x96: \"–\", 0x97: \"—\",\n 0x98: \"˜\", 0x99: \"™\", 0x9a: \"š\", 0x9b: \"›\", 0x9c: \"œ\",\n 0x9e: \"ž\", 0x9f: \"Ÿ\",\n};\n\nconst CONTROL_WORD_REGEX = /\\\\([a-zA-Z]{1,32})(-?\\d{1,10})? ?/y;\n\n/**\n * Best-effort plain-text extraction from an RTF document.\n *\n * Handles paragraphs/line breaks/tabs, escaped braces and backslashes,\n * `\\'hh` hex escapes (decoded as Windows-1252), `\\uN` Unicode escapes with\n * their `\\ucN` fallback characters, common typographic control words, and\n * skips metadata destinations (font/colour tables, `\\info`, pictures, field\n * instructions, `\\*` destinations). Formatting (bold, lists, tables) is not\n * preserved. Input that does not start with `{\\rtf` is returned unchanged.\n */\nexport function rtfToPlainText(rtf: string): string {\n if (!rtf) return \"\";\n if (!/^\\s*\\{\\\\rtf/.test(rtf)) return rtf;\n\n let out = \"\";\n const stack: Array<{ skip: boolean; uc: number }> = [];\n let skip = false;\n // Number of fallback characters that follow a \\uN escape.\n let uc = 1;\n let pendingFallback = 0;\n // True right after `{`, so a destination control word can mark the group.\n let groupStart = false;\n\n const emit = (text: string) => {\n if (skip) return;\n if (pendingFallback > 0) {\n pendingFallback--;\n return;\n }\n out += text;\n };\n\n let i = 0;\n while (i < rtf.length) {\n const ch = rtf[i];\n if (ch === \"{\") {\n stack.push({ skip, uc });\n groupStart = true;\n i++;\n continue;\n }\n if (ch === \"}\") {\n const prev = stack.pop();\n if (prev) {\n skip = prev.skip;\n uc = prev.uc;\n }\n pendingFallback = 0;\n groupStart = false;\n i++;\n continue;\n }\n const atGroupStart = groupStart;\n groupStart = false;\n\n if (ch === \"\\\\\") {\n const next = rtf[i + 1];\n if (next === \"\\\\\" || next === \"{\" || next === \"}\") {\n emit(next);\n i += 2;\n continue;\n }\n if (next === \"*\") {\n // `{\\*\\dest …}`: an ignorable destination this reader doesn't know.\n skip = true;\n i += 2;\n continue;\n }\n if (next === \"'\") {\n const code = parseInt(rtf.slice(i + 2, i + 4), 16);\n if (!Number.isNaN(code)) emit(CP1252[code] ?? String.fromCharCode(code));\n i += 4;\n continue;\n }\n if (next === \"~\") {\n emit(\" \");\n i += 2;\n continue;\n }\n if (next === \"_\") {\n emit(\"‑\");\n i += 2;\n continue;\n }\n if (next === \"\\n\" || next === \"\\r\") {\n emit(\"\\n\");\n i += 2;\n continue;\n }\n CONTROL_WORD_REGEX.lastIndex = i;\n const match = CONTROL_WORD_REGEX.exec(rtf);\n if (!match) {\n // Other control symbols (`\\-`, `\\|`, `\\:`) produce no text.\n i += 2;\n continue;\n }\n i = CONTROL_WORD_REGEX.lastIndex;\n const word = match[1];\n const param = match[2] !== undefined ? parseInt(match[2], 10) : undefined;\n if (atGroupStart && SKIPPED_DESTINATIONS.has(word)) {\n skip = true;\n } else if (word === \"u\" && param !== undefined) {\n emit(String.fromCharCode(param < 0 ? param + 65536 : param));\n if (!skip) pendingFallback = uc;\n } else if (word === \"uc\" && param !== undefined) {\n uc = param;\n } else if (word === \"bin\" && param !== undefined) {\n // Raw binary data: skip it.\n i += param;\n } else if (CONTROL_WORD_TEXT[word] !== undefined) {\n emit(CONTROL_WORD_TEXT[word]);\n }\n continue;\n }\n // Raw newlines are formatting in RTF; only control words break lines.\n if (ch === \"\\n\" || ch === \"\\r\") {\n i++;\n continue;\n }\n emit(ch);\n i++;\n }\n\n return out.replace(/\\n+$/, \"\");\n}\n","import { Slice, type Schema } from \"@tiptap/pm/model\";\n\n/**\n * Custom clipboard MIME type for content copied from a @pixerate/editor\n * rich-text editor. It carries the ProseMirror slice as JSON so pasting back\n * into a compatible editor is lossless (no HTML round trip).\n */\nexport const PIXERATE_DOCUMENT_MIME = \"application/x.pixerate.document+json\";\n\n/** Payload written under {@link PIXERATE_DOCUMENT_MIME}. */\nexport interface RichClipboardPayload {\n version: 1;\n /** {@link getSchemaSignature} of the source editor's schema. */\n schema: string;\n /** `Slice.toJSON()` of the copied selection. */\n slice: { content?: unknown[]; openStart?: number; openEnd?: number } | null;\n}\n\nconst signatureCache = new WeakMap<Schema, string>();\n\n/** 32-bit FNV-1a, enough to tell schemas apart without a crypto dependency. */\nfunction fnv1a(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Returns a stable signature for a ProseMirror schema: node names, content\n * expressions, groups, attribute names and mark names. Two editors with the\n * same extensions produce the same signature, so a slice copied from one can\n * be deserialized in the other without losing or rejecting nodes.\n */\nexport function getSchemaSignature(schema: Schema): string {\n const cached = signatureCache.get(schema);\n if (cached) return cached;\n const parts: string[] = [];\n for (const [name, type] of Object.entries(schema.nodes)) {\n const spec = type.spec;\n parts.push(\n `n:${name}|${spec.content ?? \"\"}|${spec.group ?? \"\"}|${spec.marks ?? \"\"}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`,\n );\n }\n for (const [name, type] of Object.entries(schema.marks)) {\n parts.push(`m:${name}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`);\n }\n const signature = `pm1-${fnv1a(parts.join(\";\"))}`;\n signatureCache.set(schema, signature);\n return signature;\n}\n\n/** Serializes a slice into the {@link PIXERATE_DOCUMENT_MIME} payload string. */\nexport function serializeRichClipboardPayload(slice: Slice, schema: Schema): string {\n const payload: RichClipboardPayload = {\n version: 1,\n schema: getSchemaSignature(schema),\n slice: slice.toJSON(),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Parses a {@link PIXERATE_DOCUMENT_MIME} payload into a slice for `schema`.\n * Returns `null` (so callers fall back to HTML / plain text) when the payload\n * is malformed, from another payload version, or from an editor whose schema\n * signature differs.\n */\nexport function parseRichClipboardPayload(data: string | null | undefined, schema: Schema): Slice | null {\n if (!data) return null;\n try {\n const payload = JSON.parse(data) as Partial<RichClipboardPayload>;\n if (!payload || payload.version !== 1) return null;\n if (payload.schema !== getSchemaSignature(schema)) return null;\n if (!payload.slice) return null;\n return Slice.fromJSON(schema, payload.slice as any);\n } catch {\n return null;\n }\n}\n\n/** Minimal clipboard data surface (`DataTransfer`). */\nexport interface ClipboardDataLike {\n getData(format: string): string;\n setData(format: string, data: string): void;\n clearData?(format?: string): void;\n}\n\n/**\n * Returns the `clipboardData` of a native `ClipboardEvent` or of a React\n * synthetic clipboard event (which wraps the native event in `nativeEvent`).\n */\nexport function getClipboardData(event: unknown): ClipboardDataLike | null {\n if (!event || typeof event !== \"object\") return null;\n const native = \"nativeEvent\" in event ? (event as any).nativeEvent : event;\n return ((event as any).clipboardData ?? native?.clipboardData ?? null) as ClipboardDataLike | null;\n}\n\n/**\n * Reads a slice from a paste event's {@link PIXERATE_DOCUMENT_MIME} data.\n * Returns `null` when the data is missing or its schema does not match.\n */\nexport function readRichClipboard(\n event: unknown,\n schema: Schema,\n mimeType: string = PIXERATE_DOCUMENT_MIME,\n): Slice | null {\n const data = getClipboardData(event);\n if (!data) return null;\n let raw: string | undefined;\n try {\n raw = data.getData(mimeType);\n } catch {\n return null;\n }\n return parseRichClipboardPayload(raw, schema);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-RRREAOVM.cjs","../src/serializers/html.ts","../src/serializers/rtf.ts","../src/serializers/clipboard.ts"],"names":[],"mappings":"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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-RRREAOVM.cjs","sourcesContent":[null,"import { sanitizeHtml } from \"./markdown\";\nimport { escapeHtml } from \"./plain-text\";\n\n/**\n * Elements whose content is never document text (scripts, styles, metadata,\n * embedded objects, form controls). They are removed together with their\n * content before sanitizing, so e.g. CSS rules don't show up as paragraphs.\n */\nconst DROP_WITH_CONTENT_REGEX =\n /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\n/** An unclosed `<script>` / `<style>` swallows the rest of the document. */\nconst UNCLOSED_RAW_TEXT_REGEX = /<(script|style)\\b[^>]*>[\\s\\S]*$/i;\nconst BODY_REGEX = /<body\\b[^>]*>([\\s\\S]*?)(?:<\\/body\\s*>|$)/i;\n/** Namespaced tags such as Word's `<o:p>`, which the sanitizer cannot parse. */\nconst NAMESPACED_TAG_REGEX = /<\\/?[a-zA-Z][\\w-]*:[\\w-]+\\b[^>]*>/g;\n\n/**\n * Sanitizes a full HTML document or fragment for insertion into the editor,\n * e.g. an imported `.html` file or HTML from another application.\n *\n * - Keeps only the `<body>` content when the input is a full document.\n * - Removes comments, doctype/XML declarations, CDATA, scripts, styles and\n * other non-text elements together with their content.\n * - Applies the same tag/attribute/URL allowlist as embedded HTML in markdown\n * (see `sanitizeHtml`), but strips disallowed tags instead of escaping them,\n * so wrappers such as `<section>` or `<font>` keep their text.\n *\n * It is string-based (no live DOM), so handlers such as `<img onerror>` never run.\n */\nexport function sanitizeImportedHtml(html: string): string {\n if (!html) return \"<p></p>\";\n let content = html.replace(/^/, \"\");\n content = content\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, \"\")\n .replace(/<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>|$)/g, \"\")\n .replace(/<![^>]*>/g, \"\")\n .replace(/<\\?[\\s\\S]*?(?:\\?>|$)/g, \"\");\n const body = BODY_REGEX.exec(content);\n if (body) content = body[1];\n content = content\n .replace(DROP_WITH_CONTENT_REGEX, \"\")\n .replace(UNCLOSED_RAW_TEXT_REGEX, \"\")\n .replace(NAMESPACED_TAG_REGEX, \"\");\n const sanitized = sanitizeHtml(content, { disallowedTags: \"strip\" }).trim();\n return sanitized || \"<p></p>\";\n}\n\n/**\n * Converts untrusted plain text to TipTap paragraph HTML, always escaping it.\n *\n * Unlike `plainTextToTipTapHtml`, text that already looks like `<p>…</p>` is\n * not passed through as HTML, which matters for imported files.\n */\nexport function plainTextToParagraphHtml(text: string): string {\n if (!text) return \"<p></p>\";\n return text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => `<p>${escapeHtml(line)}</p>`)\n .join(\"\");\n}\n","/**\n * Destinations whose content is metadata or binary data rather than document\n * text. A group starting with one of these control words (or with `\\*`) is\n * skipped entirely.\n */\nconst SKIPPED_DESTINATIONS = new Set([\n \"author\", \"bkmkend\", \"bkmkstart\", \"colortbl\", \"comment\", \"datastore\",\n \"doccomm\", \"falt\", \"fldinst\", \"filetbl\", \"fonttbl\", \"footer\", \"footerf\",\n \"footerl\", \"footerr\", \"footnote\", \"generator\", \"header\", \"headerf\",\n \"headerl\", \"headerr\", \"info\", \"keywords\", \"latentstyles\", \"listoverridetable\",\n \"listtable\", \"nonshppict\", \"object\", \"operator\", \"pict\", \"private\", \"revtbl\",\n \"rsidtbl\", \"shppict\", \"stylesheet\", \"subject\", \"themedata\", \"title\",\n \"xmlnstbl\", \"colorschememapping\",\n]);\n\n/** Control words that map to a literal character. */\nconst CONTROL_WORD_TEXT: Record<string, string> = {\n par: \"\\n\",\n sect: \"\\n\",\n page: \"\\n\",\n line: \"\\n\",\n row: \"\\n\",\n cell: \"\\t\",\n tab: \"\\t\",\n emdash: \"—\",\n endash: \"–\",\n emspace: \" \",\n enspace: \" \",\n bullet: \"•\",\n lquote: \"‘\",\n rquote: \"’\",\n ldblquote: \"“\",\n rdblquote: \"”\",\n};\n\n/** Windows-1252 code points in 0x80–0x9F that differ from Latin-1. */\nconst CP1252: Record<number, string> = {\n 0x80: \"€\", 0x82: \"‚\", 0x83: \"ƒ\", 0x84: \"„\", 0x85: \"…\",\n 0x86: \"†\", 0x87: \"‡\", 0x88: \"ˆ\", 0x89: \"‰\", 0x8a: \"Š\",\n 0x8b: \"‹\", 0x8c: \"Œ\", 0x8e: \"Ž\", 0x91: \"‘\", 0x92: \"’\",\n 0x93: \"“\", 0x94: \"”\", 0x95: \"•\", 0x96: \"–\", 0x97: \"—\",\n 0x98: \"˜\", 0x99: \"™\", 0x9a: \"š\", 0x9b: \"›\", 0x9c: \"œ\",\n 0x9e: \"ž\", 0x9f: \"Ÿ\",\n};\n\nconst CONTROL_WORD_REGEX = /\\\\([a-zA-Z]{1,32})(-?\\d{1,10})? ?/y;\n\n/**\n * Best-effort plain-text extraction from an RTF document.\n *\n * Handles paragraphs/line breaks/tabs, escaped braces and backslashes,\n * `\\'hh` hex escapes (decoded as Windows-1252), `\\uN` Unicode escapes with\n * their `\\ucN` fallback characters, common typographic control words, and\n * skips metadata destinations (font/colour tables, `\\info`, pictures, field\n * instructions, `\\*` destinations). Formatting (bold, lists, tables) is not\n * preserved. Input that does not start with `{\\rtf` is returned unchanged.\n */\nexport function rtfToPlainText(rtf: string): string {\n if (!rtf) return \"\";\n if (!/^\\s*\\{\\\\rtf/.test(rtf)) return rtf;\n\n let out = \"\";\n const stack: Array<{ skip: boolean; uc: number }> = [];\n let skip = false;\n // Number of fallback characters that follow a \\uN escape.\n let uc = 1;\n let pendingFallback = 0;\n // True right after `{`, so a destination control word can mark the group.\n let groupStart = false;\n\n const emit = (text: string) => {\n if (skip) return;\n if (pendingFallback > 0) {\n pendingFallback--;\n return;\n }\n out += text;\n };\n\n let i = 0;\n while (i < rtf.length) {\n const ch = rtf[i];\n if (ch === \"{\") {\n stack.push({ skip, uc });\n groupStart = true;\n i++;\n continue;\n }\n if (ch === \"}\") {\n const prev = stack.pop();\n if (prev) {\n skip = prev.skip;\n uc = prev.uc;\n }\n pendingFallback = 0;\n groupStart = false;\n i++;\n continue;\n }\n const atGroupStart = groupStart;\n groupStart = false;\n\n if (ch === \"\\\\\") {\n const next = rtf[i + 1];\n if (next === \"\\\\\" || next === \"{\" || next === \"}\") {\n emit(next);\n i += 2;\n continue;\n }\n if (next === \"*\") {\n // `{\\*\\dest …}`: an ignorable destination this reader doesn't know.\n skip = true;\n i += 2;\n continue;\n }\n if (next === \"'\") {\n const code = parseInt(rtf.slice(i + 2, i + 4), 16);\n if (!Number.isNaN(code)) emit(CP1252[code] ?? String.fromCharCode(code));\n i += 4;\n continue;\n }\n if (next === \"~\") {\n emit(\" \");\n i += 2;\n continue;\n }\n if (next === \"_\") {\n emit(\"‑\");\n i += 2;\n continue;\n }\n if (next === \"\\n\" || next === \"\\r\") {\n emit(\"\\n\");\n i += 2;\n continue;\n }\n CONTROL_WORD_REGEX.lastIndex = i;\n const match = CONTROL_WORD_REGEX.exec(rtf);\n if (!match) {\n // Other control symbols (`\\-`, `\\|`, `\\:`) produce no text.\n i += 2;\n continue;\n }\n i = CONTROL_WORD_REGEX.lastIndex;\n const word = match[1];\n const param = match[2] !== undefined ? parseInt(match[2], 10) : undefined;\n if (atGroupStart && SKIPPED_DESTINATIONS.has(word)) {\n skip = true;\n } else if (word === \"u\" && param !== undefined) {\n emit(String.fromCharCode(param < 0 ? param + 65536 : param));\n if (!skip) pendingFallback = uc;\n } else if (word === \"uc\" && param !== undefined) {\n uc = param;\n } else if (word === \"bin\" && param !== undefined) {\n // Raw binary data: skip it.\n i += param;\n } else if (CONTROL_WORD_TEXT[word] !== undefined) {\n emit(CONTROL_WORD_TEXT[word]);\n }\n continue;\n }\n // Raw newlines are formatting in RTF; only control words break lines.\n if (ch === \"\\n\" || ch === \"\\r\") {\n i++;\n continue;\n }\n emit(ch);\n i++;\n }\n\n return out.replace(/\\n+$/, \"\");\n}\n","import { Slice, type Schema } from \"@tiptap/pm/model\";\n\n/**\n * Custom clipboard MIME type for content copied from a @pixerate/editor\n * rich-text editor. It carries the ProseMirror slice as JSON so pasting back\n * into a compatible editor is lossless (no HTML round trip).\n */\nexport const PIXERATE_DOCUMENT_MIME = \"application/x.pixerate.document+json\";\n\n/** Payload written under {@link PIXERATE_DOCUMENT_MIME}. */\nexport interface RichClipboardPayload {\n version: 1;\n /** {@link getSchemaSignature} of the source editor's schema. */\n schema: string;\n /** `Slice.toJSON()` of the copied selection. */\n slice: { content?: unknown[]; openStart?: number; openEnd?: number } | null;\n}\n\nconst signatureCache = new WeakMap<Schema, string>();\n\n/** 32-bit FNV-1a, enough to tell schemas apart without a crypto dependency. */\nfunction fnv1a(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Returns a stable signature for a ProseMirror schema: node names, content\n * expressions, groups, attribute names and mark names. Two editors with the\n * same extensions produce the same signature, so a slice copied from one can\n * be deserialized in the other without losing or rejecting nodes.\n */\nexport function getSchemaSignature(schema: Schema): string {\n const cached = signatureCache.get(schema);\n if (cached) return cached;\n const parts: string[] = [];\n for (const [name, type] of Object.entries(schema.nodes)) {\n const spec = type.spec;\n parts.push(\n `n:${name}|${spec.content ?? \"\"}|${spec.group ?? \"\"}|${spec.marks ?? \"\"}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`,\n );\n }\n for (const [name, type] of Object.entries(schema.marks)) {\n parts.push(`m:${name}|${Object.keys(type.spec.attrs ?? {}).sort().join(\",\")}`);\n }\n const signature = `pm1-${fnv1a(parts.join(\";\"))}`;\n signatureCache.set(schema, signature);\n return signature;\n}\n\n/** Serializes a slice into the {@link PIXERATE_DOCUMENT_MIME} payload string. */\nexport function serializeRichClipboardPayload(slice: Slice, schema: Schema): string {\n const payload: RichClipboardPayload = {\n version: 1,\n schema: getSchemaSignature(schema),\n slice: slice.toJSON(),\n };\n return JSON.stringify(payload);\n}\n\n/**\n * Parses a {@link PIXERATE_DOCUMENT_MIME} payload into a slice for `schema`.\n * Returns `null` (so callers fall back to HTML / plain text) when the payload\n * is malformed, from another payload version, or from an editor whose schema\n * signature differs.\n */\nexport function parseRichClipboardPayload(data: string | null | undefined, schema: Schema): Slice | null {\n if (!data) return null;\n try {\n const payload = JSON.parse(data) as Partial<RichClipboardPayload>;\n if (!payload || payload.version !== 1) return null;\n if (payload.schema !== getSchemaSignature(schema)) return null;\n if (!payload.slice) return null;\n return Slice.fromJSON(schema, payload.slice as any);\n } catch {\n return null;\n }\n}\n\n/** Minimal clipboard data surface (`DataTransfer`). */\nexport interface ClipboardDataLike {\n getData(format: string): string;\n setData(format: string, data: string): void;\n clearData?(format?: string): void;\n}\n\n/**\n * Returns the `clipboardData` of a native `ClipboardEvent` or of a React\n * synthetic clipboard event (which wraps the native event in `nativeEvent`).\n */\nexport function getClipboardData(event: unknown): ClipboardDataLike | null {\n if (!event || typeof event !== \"object\") return null;\n const native = \"nativeEvent\" in event ? (event as any).nativeEvent : event;\n return ((event as any).clipboardData ?? native?.clipboardData ?? null) as ClipboardDataLike | null;\n}\n\n/**\n * Reads a slice from a paste event's {@link PIXERATE_DOCUMENT_MIME} data.\n * Returns `null` when the data is missing or its schema does not match.\n */\nexport function readRichClipboard(\n event: unknown,\n schema: Schema,\n mimeType: string = PIXERATE_DOCUMENT_MIME,\n): Slice | null {\n const data = getClipboardData(event);\n if (!data) return null;\n let raw: string | undefined;\n try {\n raw = data.getData(mimeType);\n } catch {\n return null;\n }\n return parseRichClipboardPayload(raw, schema);\n}\n"]}
|