@lobehub/editor 4.24.1 → 4.25.1
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/es/codemirror/style.js +42 -0
- package/es/editor-kernel/kernel.d.ts +9 -0
- package/es/editor-kernel/kernel.js +74 -11
- package/es/editor-kernel/react/useDecorators.js +2 -4
- package/es/headless/collapsible-plugin.js +74 -0
- package/es/headless/index.js +2 -0
- package/es/headless.d.ts +30 -0
- package/es/headless.js +2618 -139
- package/es/index.d.ts +26 -3
- package/es/index.js +17 -2
- package/es/locale/index.d.ts +14 -0
- package/es/locale/index.js +14 -0
- package/es/plugins/block/command/index.js +24 -0
- package/es/plugins/block/plugin/index.js +7 -5
- package/es/plugins/block/react/ReactBlockPlugin.js +43 -1
- package/es/plugins/block/react/core/menu-position.js +20 -0
- package/es/plugins/block/react/drag/drag-session.js +3 -2
- package/es/plugins/block/react/drag/drag-utils.js +13 -1
- package/es/plugins/codemirror-block/index.d.ts +2 -1
- package/es/plugins/codemirror-block/plugin/index.js +2 -0
- package/es/plugins/codemirror-block/react/CodemirrorNode.js +95 -5
- package/es/plugins/codemirror-block/react/useCodemirrorEditLock.js +33 -0
- package/es/plugins/codemirror-block/service/index.d.ts +40 -0
- package/es/plugins/codemirror-block/service/index.js +59 -0
- package/es/plugins/collapsible/command/index.d.ts +17 -0
- package/es/plugins/collapsible/command/index.js +72 -0
- package/es/plugins/collapsible/index.d.ts +7 -0
- package/es/plugins/collapsible/node/CollapsibleNode.d.ts +30 -0
- package/es/plugins/collapsible/node/CollapsibleNode.js +138 -0
- package/es/plugins/collapsible/plugin/index.d.ts +11 -0
- package/es/plugins/collapsible/plugin/index.js +535 -0
- package/es/plugins/collapsible/react/ReactCollapsiblePlugin.d.ts +6 -0
- package/es/plugins/collapsible/react/ReactCollapsiblePlugin.js +19 -0
- package/es/plugins/collapsible/react/index.d.ts +3 -0
- package/es/plugins/collapsible/react/style.js +67 -0
- package/es/plugins/collapsible/react/type.d.ts +6 -0
- package/es/plugins/common/plugin/index.js +14 -4
- package/es/plugins/common/plugin/progressive-select-all.js +81 -0
- package/es/plugins/common/plugin/register.js +73 -4
- package/es/plugins/common/react/ReactPlainText.js +27 -9
- package/es/plugins/link/command/index.js +26 -3
- package/es/plugins/link/conversion/index.js +237 -0
- package/es/plugins/link/index.d.ts +5 -3
- package/es/plugins/link/node/LinkBlockCardNode.d.ts +16 -0
- package/es/plugins/link/node/LinkBlockCardNode.js +66 -0
- package/es/plugins/link/node/LinkCardNode.d.ts +42 -0
- package/es/plugins/link/node/LinkCardNode.js +161 -0
- package/es/plugins/link/node/LinkIframeNode.d.ts +33 -0
- package/es/plugins/link/node/LinkIframeNode.js +131 -0
- package/es/plugins/link/node/LinkNode.d.ts +48 -2
- package/es/plugins/link/node/LinkNode.js +14 -6
- package/es/plugins/link/node/SchemaNode.d.ts +36 -0
- package/es/plugins/link/node/SchemaNode.js +149 -0
- package/es/plugins/link/normalization/index.d.ts +7 -0
- package/es/plugins/link/normalization/index.js +38 -0
- package/es/plugins/link/plugin/index.d.ts +26 -1
- package/es/plugins/link/plugin/index.js +124 -4
- package/es/plugins/link/react/ReactLinkPlugin.js +165 -7
- package/es/plugins/link/react/components/LinkCard.js +235 -0
- package/es/plugins/link/react/components/LinkEdit.js +93 -14
- package/es/plugins/link/react/components/LinkIframe.js +128 -0
- package/es/plugins/link/react/components/LinkToolbar.js +440 -111
- package/es/plugins/link/react/components/SchemaLink.js +49 -0
- package/es/plugins/link/react/index.d.ts +2 -2
- package/es/plugins/link/react/renderer-registry.d.ts +81 -0
- package/es/plugins/link/react/renderer-registry.js +80 -0
- package/es/plugins/link/react/style.js +63 -1
- package/es/plugins/link/react/type.d.ts +24 -1
- package/es/plugins/link/service/i-link-service.d.ts +156 -1
- package/es/plugins/link/service/i-link-service.js +258 -5
- package/es/plugins/list/plugin/index.js +9 -2
- package/es/plugins/litexml/command/diffCommand.js +94 -14
- package/es/plugins/litexml/command/index.js +187 -6
- package/es/plugins/litexml/diff-validation.js +237 -0
- package/es/plugins/litexml/node/DiffContentNode.js +67 -0
- package/es/plugins/litexml/node/DiffNode.d.ts +1 -1
- package/es/plugins/litexml/node/DiffNode.js +5 -2
- package/es/plugins/litexml/node/TableCellDiffNode.js +77 -0
- package/es/plugins/litexml/node/TableRowDiffNode.js +73 -0
- package/es/plugins/litexml/plugin/index.d.ts +2 -0
- package/es/plugins/litexml/plugin/index.js +62 -9
- package/es/plugins/litexml/react/DiffNodeToolbar/index.js +10 -12
- package/es/plugins/litexml/react/TableRowDiffToolbar.js +123 -0
- package/es/plugins/litexml/react/hooks/useHasDiffNode.js +5 -2
- package/es/plugins/litexml/react/index.js +6 -2
- package/es/plugins/litexml/react/style.js +18 -5
- package/es/plugins/litexml/table-cell-diff.js +153 -0
- package/es/plugins/litexml/table-row-diff.js +82 -0
- package/es/plugins/markdown/data-source/markdown/parse.js +25 -1
- package/es/plugins/markdown/plugin/index.js +5 -3
- package/es/plugins/slash/react/ReactSlashPlugin.js +22 -43
- package/es/plugins/slash/react/components/DefaultSlashMenu.js +76 -16
- package/es/plugins/slash/react/menu-utils.js +84 -0
- package/es/plugins/slash/service/i-slash-service.d.ts +3 -0
- package/es/plugins/table/plugin/index.js +4 -1
- package/es/plugins/table/plugin/single-cell-paste.js +79 -0
- package/es/plugins/table/react/TableController/hooks.js +7 -4
- package/es/plugins/table/react/hooks.js +2 -2
- package/es/plugins/table/react/index.js +2 -0
- package/es/plugins/table/react/style.js +15 -5
- package/es/plugins/table/react/useScrollFocusedTableCellIntoView.js +46 -0
- package/es/plugins/toc/index.d.ts +10 -0
- package/es/plugins/toc/plugin/index.d.ts +8 -0
- package/es/plugins/toc/plugin/index.js +56 -0
- package/es/plugins/toc/react/ReactTocPlugin.d.ts +6 -0
- package/es/plugins/toc/react/ReactTocPlugin.js +36 -0
- package/es/plugins/toc/react/getNearestScrollContainer.d.ts +6 -0
- package/es/plugins/toc/react/getNearestScrollContainer.js +18 -0
- package/es/plugins/toc/react/hooks/useActiveHeading.js +59 -0
- package/es/plugins/toc/react/hooks/useTocItems.js +50 -0
- package/es/plugins/toc/react/index.d.ts +5 -0
- package/es/plugins/toc/react/type.d.ts +30 -0
- package/es/plugins/toc/react/useToc.d.ts +5 -0
- package/es/plugins/toc/react/useToc.js +41 -0
- package/es/plugins/toc/service/i-toc-service.d.ts +39 -0
- package/es/plugins/toc/service/i-toc-service.js +125 -0
- package/es/plugins/toc/service/index.d.ts +3 -0
- package/es/plugins/toc/types.d.ts +23 -0
- package/es/plugins/toc/utils/index.d.ts +6 -0
- package/es/plugins/toc/utils/index.js +30 -0
- package/es/plugins/toolbar/react/index.js +26 -12
- package/es/plugins/toolbar/react/style.js +3 -2
- package/es/plugins/virtual-block/plugin/register.js +34 -12
- package/es/plugins/yjs/index.d.ts +6 -0
- package/es/plugins/yjs/plugin/index.d.ts +7 -0
- package/es/plugins/yjs/plugin/index.js +191 -0
- package/es/plugins/yjs/plugin/types.d.ts +16 -0
- package/es/plugins/yjs/plugin/utils/awareness.js +9 -0
- package/es/plugins/yjs/plugin/utils/editor-state.js +47 -0
- package/es/plugins/yjs/plugin/utils/history.js +31 -0
- package/es/plugins/yjs/plugin/utils/node-properties.js +39 -0
- package/es/plugins/yjs/plugin/utils/sync.js +24 -0
- package/es/plugins/yjs/react/codemirrorEditLockProvider.js +59 -0
- package/es/plugins/yjs/react/index.d.ts +23 -0
- package/es/plugins/yjs/react/index.js +200 -0
- package/es/plugins/yjs/service/index.d.ts +47 -0
- package/es/plugins/yjs/service/index.js +84 -0
- package/es/plugins/yjs/websocket-provider.d.ts +78 -0
- package/es/plugins/yjs/websocket-provider.js +263 -0
- package/es/react/hooks/useEditor.d.ts +24 -2
- package/es/react/hooks/useEditor.js +13 -3
- package/es/react.d.ts +2 -2
- package/es/renderer/LexicalRenderer.js +9 -2
- package/es/types/kernel.d.ts +16 -0
- package/es/utils/updatePosition.js +5 -2
- package/package.json +3 -1
- package/patches/@lexical__yjs@0.42.0.patch +42 -0
- package/scripts/postinstall-lexical-patch.cjs +62 -36
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
//#region src/plugins/litexml/diff-validation.ts
|
|
2
|
+
const ACTIONABLE_DIFF_TYPES = /* @__PURE__ */ new Set([
|
|
3
|
+
"add",
|
|
4
|
+
"remove",
|
|
5
|
+
"modify",
|
|
6
|
+
"listItemAdd",
|
|
7
|
+
"listItemModify",
|
|
8
|
+
"listItemRemove"
|
|
9
|
+
]);
|
|
10
|
+
const isActionableDiff = (node) => node?.type === "diff" && ACTIONABLE_DIFF_TYPES.has(node.diffType || "");
|
|
11
|
+
const isElementNode = (node) => Array.isArray(node.children);
|
|
12
|
+
const cloneNode = (node) => structuredClone(node);
|
|
13
|
+
function hasActionableDiffDescendant(node) {
|
|
14
|
+
return (node.children || []).some((child) => isActionableDiff(child) || hasActionableDiffDescendant(child));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate the serialized public tree. Paths use serialized node ids, not
|
|
18
|
+
* Lexical's private node map, so this remains stable across JSON reloads.
|
|
19
|
+
*/
|
|
20
|
+
function collectIllegalNestedDiffPaths(root) {
|
|
21
|
+
const violations = [];
|
|
22
|
+
const visit = (node, ancestors, path) => {
|
|
23
|
+
const actionableAncestor = [...ancestors].reverse().find(isActionableDiff);
|
|
24
|
+
const rowDiffAncestor = [...ancestors].reverse().find((ancestor) => ancestor.type === "table-row-diff");
|
|
25
|
+
const allowedCellDiffChild = ancestors.at(-1)?.type === "table-cell-diff" && isActionableDiff(node);
|
|
26
|
+
if (isActionableDiff(node) && actionableAncestor && !allowedCellDiffChild) violations.push(`${path}:diff-inside-diff`);
|
|
27
|
+
if (rowDiffAncestor) {
|
|
28
|
+
if (node.type === "table-cell-diff") violations.push(`${path}:cell-inside-row-diff`);
|
|
29
|
+
else if (isActionableDiff(node)) violations.push(`${path}:diff-inside-row-diff`);
|
|
30
|
+
}
|
|
31
|
+
if (actionableAncestor) {
|
|
32
|
+
if (node.type === "table-cell-diff") violations.push(`${path}:cell-diff-inside-diff`);
|
|
33
|
+
else if (node.type === "table-row-diff") violations.push(`${path}:row-diff-inside-diff`);
|
|
34
|
+
}
|
|
35
|
+
if (!isElementNode(node)) return;
|
|
36
|
+
node.children?.forEach((child) => {
|
|
37
|
+
visit(child, [...ancestors, node], `${path}/${child.id || child.type || "node"}`);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
visit(root, [], root.id || root.type || "root");
|
|
41
|
+
return violations;
|
|
42
|
+
}
|
|
43
|
+
function findNewIllegalDiffPaths(previous, projected) {
|
|
44
|
+
const previousCounts = /* @__PURE__ */ new Map();
|
|
45
|
+
collectIllegalNestedDiffPaths(previous.root).forEach((path) => {
|
|
46
|
+
previousCounts.set(path, (previousCounts.get(path) || 0) + 1);
|
|
47
|
+
});
|
|
48
|
+
const newPaths = [];
|
|
49
|
+
const projectedCounts = /* @__PURE__ */ new Map();
|
|
50
|
+
collectIllegalNestedDiffPaths(projected.root).forEach((path) => {
|
|
51
|
+
const count = (projectedCounts.get(path) || 0) + 1;
|
|
52
|
+
projectedCounts.set(path, count);
|
|
53
|
+
if (count > (previousCounts.get(path) || 0)) newPaths.push(path);
|
|
54
|
+
});
|
|
55
|
+
return newPaths;
|
|
56
|
+
}
|
|
57
|
+
function findNodeLocation(root, id, ancestors = []) {
|
|
58
|
+
if (root.id === id) return {
|
|
59
|
+
ancestors,
|
|
60
|
+
index: -1,
|
|
61
|
+
node: root,
|
|
62
|
+
parent: null
|
|
63
|
+
};
|
|
64
|
+
for (const [index, child] of (root.children || []).entries()) {
|
|
65
|
+
if (child.id === id) return {
|
|
66
|
+
ancestors: [...ancestors, root],
|
|
67
|
+
index,
|
|
68
|
+
node: child,
|
|
69
|
+
parent: root
|
|
70
|
+
};
|
|
71
|
+
const nested = findNodeLocation(child, id, [...ancestors, root]);
|
|
72
|
+
if (nested) return nested;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function replaceNode(location, replacements) {
|
|
77
|
+
if (!location.parent || location.index < 0) return;
|
|
78
|
+
location.parent.children?.splice(location.index, 1, ...replacements);
|
|
79
|
+
}
|
|
80
|
+
function createDiff(diffType, children) {
|
|
81
|
+
return {
|
|
82
|
+
children,
|
|
83
|
+
diffType,
|
|
84
|
+
type: "diff"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function createDiffContent(side, children) {
|
|
88
|
+
return {
|
|
89
|
+
children,
|
|
90
|
+
side,
|
|
91
|
+
type: "diff-content"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function projectModifyTarget(root, incoming) {
|
|
95
|
+
if (!incoming.id) return;
|
|
96
|
+
const location = findNodeLocation(root, incoming.id);
|
|
97
|
+
if (!location) return;
|
|
98
|
+
const target = location.node;
|
|
99
|
+
const next = cloneNode(incoming);
|
|
100
|
+
if (target.type === "tablerow" && location.parent?.type === "table") {
|
|
101
|
+
replaceNode(location, [{
|
|
102
|
+
children: cloneNode(target.children || []),
|
|
103
|
+
diffType: "remove",
|
|
104
|
+
type: "table-row-diff"
|
|
105
|
+
}, {
|
|
106
|
+
children: cloneNode(next.children || []),
|
|
107
|
+
diffType: "add",
|
|
108
|
+
type: "table-row-diff"
|
|
109
|
+
}]);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (target.type === "tablecell" || target.type === "table-cell-diff") {
|
|
113
|
+
const existingDiff = (target.children || []).find((child) => child.type === "diff");
|
|
114
|
+
if (existingDiff && target.type === "table-cell-diff") {
|
|
115
|
+
existingDiff.children = cloneNode(next.children || []);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const after = existingDiff?.children?.find((child) => child.type === "diff-content" && child.side === "after");
|
|
119
|
+
if (existingDiff?.diffType === "modify" && after) {
|
|
120
|
+
after.children = cloneNode(next.children || []);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
target.children = [createDiff("modify", [createDiffContent("before", cloneNode(target.children || [])), createDiffContent("after", cloneNode(next.children || []))])];
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (location.ancestors.some(isActionableDiff)) {
|
|
127
|
+
replaceNode(location, [next]);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
replaceNode(location, [createDiff("modify", [cloneNode(target), next])]);
|
|
131
|
+
}
|
|
132
|
+
function projectRemoveTarget(root, id) {
|
|
133
|
+
const location = findNodeLocation(root, id);
|
|
134
|
+
if (!location) return;
|
|
135
|
+
if (location.ancestors.some(isActionableDiff)) {
|
|
136
|
+
replaceNode(location, []);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const target = location.node;
|
|
140
|
+
if (target.type === "tablecell" || target.type === "table-cell-diff") {
|
|
141
|
+
replaceNode(location, [{
|
|
142
|
+
children: [createDiff("remove", cloneNode(target.children || []))],
|
|
143
|
+
diffType: "remove",
|
|
144
|
+
type: "table-cell-diff"
|
|
145
|
+
}]);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (target.type === "tablerow" && location.parent?.type === "table") {
|
|
149
|
+
replaceNode(location, [{
|
|
150
|
+
children: cloneNode(target.children || []),
|
|
151
|
+
diffType: "remove",
|
|
152
|
+
type: "table-row-diff"
|
|
153
|
+
}]);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
replaceNode(location, [createDiff("remove", [cloneNode(target)])]);
|
|
157
|
+
}
|
|
158
|
+
function projectInsert(root, operation, readXml) {
|
|
159
|
+
const referenceId = "beforeId" in operation ? operation.beforeId : operation.afterId;
|
|
160
|
+
const reference = referenceId === "root" ? null : findNodeLocation(root, referenceId)?.node || null;
|
|
161
|
+
const location = referenceId === "root" ? {
|
|
162
|
+
ancestors: [root],
|
|
163
|
+
index: "beforeId" in operation ? 0 : (root.children || []).length - 1,
|
|
164
|
+
node: root.children?.[0] || root,
|
|
165
|
+
parent: root
|
|
166
|
+
} : findNodeLocation(root, referenceId);
|
|
167
|
+
if (!location) return;
|
|
168
|
+
const incoming = (readXml(operation.litexml).root?.children || []).map(cloneNode);
|
|
169
|
+
if (incoming.length === 0 || !location.parent) return;
|
|
170
|
+
const insertAt = "beforeId" in operation ? location.index : location.index + 1;
|
|
171
|
+
if (incoming.every((node) => node.type === "tablecell") && reference?.type === "tablecell") {
|
|
172
|
+
location.parent.children?.splice(insertAt, 0, ...incoming.map((node) => ({
|
|
173
|
+
children: [createDiff("add", cloneNode(node.children || []))],
|
|
174
|
+
diffType: "add",
|
|
175
|
+
type: "table-cell-diff"
|
|
176
|
+
})));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (incoming.every((node) => node.type === "tablerow") && reference?.type === "tablerow") {
|
|
180
|
+
location.parent.children?.splice(insertAt, 0, ...incoming.map((node) => ({
|
|
181
|
+
children: cloneNode(node.children || []),
|
|
182
|
+
diffType: "add",
|
|
183
|
+
type: "table-row-diff"
|
|
184
|
+
})));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const actionableAncestor = location.ancestors.findLast(isActionableDiff);
|
|
188
|
+
const isInlineReference = [
|
|
189
|
+
"text",
|
|
190
|
+
"span",
|
|
191
|
+
"link",
|
|
192
|
+
"autolink",
|
|
193
|
+
"linebreak"
|
|
194
|
+
].includes(reference?.type || "");
|
|
195
|
+
const blockAncestor = [...location.ancestors].reverse().find((node) => [
|
|
196
|
+
"paragraph",
|
|
197
|
+
"heading",
|
|
198
|
+
"listitem",
|
|
199
|
+
"table",
|
|
200
|
+
"tablerow",
|
|
201
|
+
"tablecell"
|
|
202
|
+
].includes(node.type || ""));
|
|
203
|
+
if (!actionableAncestor && blockAncestor && hasActionableDiffDescendant(blockAncestor)) {
|
|
204
|
+
const wrapped = createDiff("modify", [cloneNode(blockAncestor), cloneNode(blockAncestor)]);
|
|
205
|
+
const blockLocation = findNodeLocation(root, blockAncestor.id || "");
|
|
206
|
+
if (blockLocation) replaceNode(blockLocation, [wrapped]);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (actionableAncestor && !isInlineReference) {
|
|
210
|
+
const originLocation = actionableAncestor.id ? findNodeLocation(root, actionableAncestor.id) : null;
|
|
211
|
+
if (originLocation?.parent && originLocation.index >= 0) {
|
|
212
|
+
const originInsertAt = "beforeId" in operation ? originLocation.index : originLocation.index + 1;
|
|
213
|
+
originLocation.parent.children?.splice(originInsertAt, 0, ...incoming.map((node) => createDiff("add", [node])));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const inserted = actionableAncestor && isInlineReference ? incoming : incoming.map((node) => createDiff("add", [node]));
|
|
218
|
+
location.parent.children?.splice(insertAt, 0, ...inserted);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Project one operation on a cloned serialized tree. The real Lexical tree is
|
|
222
|
+
* untouched; callers compare illegal paths before/after and only dispatch safe
|
|
223
|
+
* operations to the live editor.
|
|
224
|
+
*/
|
|
225
|
+
function projectLiteXmlOperation(document, operation, readXml) {
|
|
226
|
+
const projected = cloneNode(document);
|
|
227
|
+
if (operation.action === "modify") (Array.isArray(operation.litexml) ? operation.litexml : [operation.litexml]).forEach((xml) => {
|
|
228
|
+
(readXml(xml).root?.children || []).forEach((child) => {
|
|
229
|
+
projectModifyTarget(projected.root, child);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
else if (operation.action === "remove") projectRemoveTarget(projected.root, operation.id);
|
|
233
|
+
else projectInsert(projected.root, operation, readXml);
|
|
234
|
+
return projected;
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
export { collectIllegalNestedDiffPaths, findNewIllegalDiffPaths, hasActionableDiffDescendant, projectLiteXmlOperation };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { $applyNodeReplacement, ElementNode } from "lexical";
|
|
2
|
+
//#region src/plugins/litexml/node/DiffContentNode.ts
|
|
3
|
+
/** Groups one side of a modify diff so it can contain any number of block nodes. */
|
|
4
|
+
var DiffContentNode = class DiffContentNode extends ElementNode {
|
|
5
|
+
static getType() {
|
|
6
|
+
return "diff-content";
|
|
7
|
+
}
|
|
8
|
+
static clone(node) {
|
|
9
|
+
return new DiffContentNode(node.__side, node.__key);
|
|
10
|
+
}
|
|
11
|
+
static importJSON(serializedNode) {
|
|
12
|
+
return $createDiffContentNode(serializedNode.side).updateFromJSON(serializedNode);
|
|
13
|
+
}
|
|
14
|
+
static importDOM() {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
constructor(side, key) {
|
|
18
|
+
super(key);
|
|
19
|
+
this.__side = side;
|
|
20
|
+
}
|
|
21
|
+
get side() {
|
|
22
|
+
return this.__side;
|
|
23
|
+
}
|
|
24
|
+
setSide(side) {
|
|
25
|
+
this.getWritable().__side = side;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
updateFromJSON(serializedNode) {
|
|
29
|
+
return super.updateFromJSON(serializedNode).setSide(serializedNode.side);
|
|
30
|
+
}
|
|
31
|
+
exportJSON() {
|
|
32
|
+
return {
|
|
33
|
+
...super.exportJSON(),
|
|
34
|
+
side: this.__side,
|
|
35
|
+
type: DiffContentNode.getType(),
|
|
36
|
+
version: 1
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
createDOM(_config, _editor) {
|
|
40
|
+
const element = document.createElement("div");
|
|
41
|
+
element.dataset.diffContent = this.__side;
|
|
42
|
+
return element;
|
|
43
|
+
}
|
|
44
|
+
updateDOM(previousNode, dom) {
|
|
45
|
+
if (previousNode.__side !== this.__side) dom.dataset.diffContent = this.__side;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
exportDOM(_editor) {
|
|
49
|
+
const element = document.createElement("div");
|
|
50
|
+
element.dataset.diffContent = this.__side;
|
|
51
|
+
return { element };
|
|
52
|
+
}
|
|
53
|
+
canBeEmpty() {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
isInline() {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function $createDiffContentNode(side) {
|
|
61
|
+
return $applyNodeReplacement(new DiffContentNode(side));
|
|
62
|
+
}
|
|
63
|
+
function $isDiffContentNode(node) {
|
|
64
|
+
return node instanceof DiffContentNode;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { $createDiffContentNode, $isDiffContentNode, DiffContentNode };
|
|
@@ -5,7 +5,7 @@ type DiffType = 'add' | 'remove' | 'modify' | 'unchanged' | 'listItemModify' | '
|
|
|
5
5
|
type SerializedDiffNode = Spread<{
|
|
6
6
|
diffType: DiffType;
|
|
7
7
|
}, SerializedElementNode>;
|
|
8
|
-
/** DiffNode - contains
|
|
8
|
+
/** DiffNode - contains the original and modified sides, optionally grouped into multiple blocks. */
|
|
9
9
|
declare class DiffNode extends CardLikeElementNode {
|
|
10
10
|
static getType(): string;
|
|
11
11
|
static clone(node: DiffNode): DiffNode;
|
|
@@ -3,7 +3,7 @@ import { CardLikeElementNode } from "../../common/node/cursor.js";
|
|
|
3
3
|
import { $applyNodeReplacement } from "lexical";
|
|
4
4
|
//#region src/plugins/litexml/node/DiffNode.ts
|
|
5
5
|
init_utils();
|
|
6
|
-
/** DiffNode - contains
|
|
6
|
+
/** DiffNode - contains the original and modified sides, optionally grouped into multiple blocks. */
|
|
7
7
|
var DiffNode = class DiffNode extends CardLikeElementNode {
|
|
8
8
|
static getType() {
|
|
9
9
|
return "diff";
|
|
@@ -95,5 +95,8 @@ var DiffNode = class DiffNode extends CardLikeElementNode {
|
|
|
95
95
|
function $createDiffNode(diffType = "unchanged") {
|
|
96
96
|
return $applyNodeReplacement(new DiffNode(diffType));
|
|
97
97
|
}
|
|
98
|
+
function $isDiffNode(node) {
|
|
99
|
+
return node instanceof DiffNode;
|
|
100
|
+
}
|
|
98
101
|
//#endregion
|
|
99
|
-
export { $createDiffNode, DiffNode };
|
|
102
|
+
export { $createDiffNode, $isDiffNode, DiffNode };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { TableCellNode } from "@lexical/table";
|
|
2
|
+
import { $applyNodeReplacement } from "lexical";
|
|
3
|
+
//#region src/plugins/litexml/node/TableCellDiffNode.ts
|
|
4
|
+
/**
|
|
5
|
+
* Review-only table cell. The diff state lives on a TableCellNode subclass so
|
|
6
|
+
* TableRowNode children remain cells throughout staging and approval.
|
|
7
|
+
*/
|
|
8
|
+
var TableCellDiffNode = class TableCellDiffNode extends TableCellNode {
|
|
9
|
+
static getType() {
|
|
10
|
+
return "table-cell-diff";
|
|
11
|
+
}
|
|
12
|
+
static clone(node) {
|
|
13
|
+
const clone = new TableCellDiffNode(node.__diffType, node.__changeId, node.getHeaderStyles(), node.getColSpan(), node.getWidth(), node.__key);
|
|
14
|
+
clone.__rowSpan = node.__rowSpan;
|
|
15
|
+
clone.__backgroundColor = node.__backgroundColor;
|
|
16
|
+
clone.__verticalAlign = node.__verticalAlign;
|
|
17
|
+
return clone;
|
|
18
|
+
}
|
|
19
|
+
static importJSON(serializedNode) {
|
|
20
|
+
return $createTableCellDiffNode(serializedNode.diffType, serializedNode.changeId, serializedNode.headerState, serializedNode.colSpan, serializedNode.width).updateFromJSON(serializedNode);
|
|
21
|
+
}
|
|
22
|
+
constructor(diffType, changeId, headerState, colSpan, width, key) {
|
|
23
|
+
super(headerState, colSpan, width, key);
|
|
24
|
+
this.__diffType = diffType;
|
|
25
|
+
this.__changeId = changeId;
|
|
26
|
+
}
|
|
27
|
+
createDOM(config) {
|
|
28
|
+
const element = super.createDOM(config);
|
|
29
|
+
const theme = config.theme.tableCellDiff;
|
|
30
|
+
if (typeof theme === "string" && theme) element.classList.add(theme);
|
|
31
|
+
this.syncDOMState(element);
|
|
32
|
+
return element;
|
|
33
|
+
}
|
|
34
|
+
updateDOM(prevNode) {
|
|
35
|
+
return super.updateDOM(prevNode) || prevNode.__diffType !== this.__diffType || prevNode.__changeId !== this.__changeId;
|
|
36
|
+
}
|
|
37
|
+
exportJSON() {
|
|
38
|
+
return {
|
|
39
|
+
...super.exportJSON(),
|
|
40
|
+
changeId: this.__changeId,
|
|
41
|
+
diffType: this.__diffType,
|
|
42
|
+
type: TableCellDiffNode.getType(),
|
|
43
|
+
version: 1
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
getChangeId() {
|
|
47
|
+
return this.getLatest().__changeId;
|
|
48
|
+
}
|
|
49
|
+
getDiffType() {
|
|
50
|
+
return this.getLatest().__diffType;
|
|
51
|
+
}
|
|
52
|
+
setChangeId(changeId) {
|
|
53
|
+
this.getWritable().__changeId = changeId;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
setDiffType(diffType) {
|
|
57
|
+
this.getWritable().__diffType = diffType;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
updateFromJSON(serializedNode) {
|
|
61
|
+
return super.updateFromJSON(serializedNode).setDiffType(serializedNode.diffType).setChangeId(serializedNode.changeId);
|
|
62
|
+
}
|
|
63
|
+
syncDOMState(element) {
|
|
64
|
+
element.contentEditable = "false";
|
|
65
|
+
element.dataset.diffType = this.__diffType;
|
|
66
|
+
if (this.__changeId) element.dataset.diffChangeId = this.__changeId;
|
|
67
|
+
else delete element.dataset.diffChangeId;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function $createTableCellDiffNode(diffType, changeId, headerState, colSpan, width) {
|
|
71
|
+
return $applyNodeReplacement(new TableCellDiffNode(diffType, changeId, headerState, colSpan, width));
|
|
72
|
+
}
|
|
73
|
+
function $isTableCellDiffNode(node) {
|
|
74
|
+
return node instanceof TableCellDiffNode;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { $createTableCellDiffNode, $isTableCellDiffNode, TableCellDiffNode };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TableRowNode } from "@lexical/table";
|
|
2
|
+
import { $applyNodeReplacement } from "lexical";
|
|
3
|
+
//#region src/plugins/litexml/node/TableRowDiffNode.ts
|
|
4
|
+
/**
|
|
5
|
+
* A review-only table row. Keeping the diff state on a TableRowNode subclass
|
|
6
|
+
* preserves Lexical's table invariant: TableNode children are always rows.
|
|
7
|
+
*/
|
|
8
|
+
var TableRowDiffNode = class TableRowDiffNode extends TableRowNode {
|
|
9
|
+
static getType() {
|
|
10
|
+
return "table-row-diff";
|
|
11
|
+
}
|
|
12
|
+
static clone(node) {
|
|
13
|
+
return new TableRowDiffNode(node.__diffType, node.__changeId, node.getHeight(), node.__key);
|
|
14
|
+
}
|
|
15
|
+
static importJSON(serializedNode) {
|
|
16
|
+
return $createTableRowDiffNode(serializedNode.diffType, serializedNode.changeId, serializedNode.height).updateFromJSON(serializedNode);
|
|
17
|
+
}
|
|
18
|
+
constructor(diffType, changeId, height, key) {
|
|
19
|
+
super(height, key);
|
|
20
|
+
this.__diffType = diffType;
|
|
21
|
+
this.__changeId = changeId;
|
|
22
|
+
}
|
|
23
|
+
createDOM(config) {
|
|
24
|
+
const element = super.createDOM(config);
|
|
25
|
+
const theme = config.theme.tableRowDiff;
|
|
26
|
+
if (typeof theme === "string" && theme) element.classList.add(theme);
|
|
27
|
+
this.syncDOMState(element);
|
|
28
|
+
return element;
|
|
29
|
+
}
|
|
30
|
+
exportJSON() {
|
|
31
|
+
return {
|
|
32
|
+
...super.exportJSON(),
|
|
33
|
+
changeId: this.__changeId,
|
|
34
|
+
diffType: this.__diffType,
|
|
35
|
+
type: TableRowDiffNode.getType(),
|
|
36
|
+
version: 1
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
getChangeId() {
|
|
40
|
+
return this.getLatest().__changeId;
|
|
41
|
+
}
|
|
42
|
+
getDiffType() {
|
|
43
|
+
return this.getLatest().__diffType;
|
|
44
|
+
}
|
|
45
|
+
setChangeId(changeId) {
|
|
46
|
+
this.getWritable().__changeId = changeId;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
setDiffType(diffType) {
|
|
50
|
+
this.getWritable().__diffType = diffType;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
updateDOM(prevNode) {
|
|
54
|
+
return super.updateDOM(prevNode) || prevNode.__diffType !== this.__diffType || prevNode.__changeId !== this.__changeId;
|
|
55
|
+
}
|
|
56
|
+
updateFromJSON(serializedNode) {
|
|
57
|
+
return super.updateFromJSON(serializedNode).setDiffType(serializedNode.diffType).setChangeId(serializedNode.changeId);
|
|
58
|
+
}
|
|
59
|
+
syncDOMState(element) {
|
|
60
|
+
element.dataset.diffType = this.__diffType;
|
|
61
|
+
if (this.__changeId) element.dataset.diffChangeId = this.__changeId;
|
|
62
|
+
else delete element.dataset.diffChangeId;
|
|
63
|
+
element.contentEditable = "false";
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function $createTableRowDiffNode(diffType, changeId, height) {
|
|
67
|
+
return $applyNodeReplacement(new TableRowDiffNode(diffType, changeId, height));
|
|
68
|
+
}
|
|
69
|
+
function $isTableRowDiffNode(node) {
|
|
70
|
+
return node instanceof TableRowDiffNode;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { $createTableRowDiffNode, $isTableRowDiffNode, TableRowDiffNode };
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { KernelPlugin, init_plugin } from "../../../editor-kernel/plugin.js";
|
|
2
|
+
import { $isDiffContentNode, DiffContentNode } from "../node/DiffContentNode.js";
|
|
2
3
|
import { DiffNode } from "../node/DiffNode.js";
|
|
4
|
+
import { $isTableCellDiffNode, TableCellDiffNode } from "../node/TableCellDiffNode.js";
|
|
5
|
+
import { registerLegacyTableCellDiffNormalization } from "../table-cell-diff.js";
|
|
6
|
+
import { $isTableRowDiffNode, TableRowDiffNode } from "../node/TableRowDiffNode.js";
|
|
7
|
+
import { registerTableRowDiffNormalization } from "../table-row-diff.js";
|
|
3
8
|
import { registerLiteXMLCommand } from "../command/index.js";
|
|
4
9
|
import { registerLiteXMLDiffCommand } from "../command/diffCommand.js";
|
|
5
10
|
import { ILitexmlService, LitexmlService } from "../service/litexml-service.js";
|
|
6
11
|
import LitexmlDataSource from "../data-source/litexml-data-source.js";
|
|
7
12
|
import { IMarkdownShortCutService } from "../../markdown/service/shortcut.js";
|
|
13
|
+
import { $isTableNode } from "@lexical/table";
|
|
8
14
|
import { $nodesOfType, HISTORIC_TAG } from "lexical";
|
|
9
15
|
//#region src/plugins/litexml/plugin/index.ts
|
|
10
16
|
init_plugin();
|
|
@@ -20,14 +26,23 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
20
26
|
super();
|
|
21
27
|
this.kernel = kernel;
|
|
22
28
|
this.config = config;
|
|
23
|
-
kernel.registerThemes({
|
|
29
|
+
kernel.registerThemes({
|
|
30
|
+
diffNode: config?.theme || "editor_diffNode",
|
|
31
|
+
tableRowDiff: config?.tableRowDiffTheme || "editor_tableRowDiff",
|
|
32
|
+
tableCellDiff: config?.tableCellDiffTheme || "editor_tableCellDiff"
|
|
33
|
+
});
|
|
24
34
|
const litexmlService = new LitexmlService();
|
|
25
35
|
this.service = litexmlService;
|
|
26
36
|
kernel.registerService(ILitexmlService, litexmlService);
|
|
27
37
|
this.datasource = new LitexmlDataSource("litexml", (serviceId) => {
|
|
28
38
|
return kernel.requireService(serviceId);
|
|
29
39
|
}, litexmlService);
|
|
30
|
-
kernel.registerNodes([
|
|
40
|
+
kernel.registerNodes([
|
|
41
|
+
DiffNode,
|
|
42
|
+
DiffContentNode,
|
|
43
|
+
TableRowDiffNode,
|
|
44
|
+
TableCellDiffNode
|
|
45
|
+
]);
|
|
31
46
|
kernel.registerDecorator(DiffNode.getType(), {
|
|
32
47
|
queryDOM: (el) => el.querySelector(".toolbar"),
|
|
33
48
|
render: (node, editor) => {
|
|
@@ -39,6 +54,8 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
39
54
|
onInit(editor) {
|
|
40
55
|
this.register(registerLiteXMLCommand(editor, this.datasource));
|
|
41
56
|
this.register(registerLiteXMLDiffCommand(editor));
|
|
57
|
+
this.register(registerLegacyTableCellDiffNormalization(editor));
|
|
58
|
+
this.register(registerTableRowDiffNormalization(editor));
|
|
42
59
|
this.register(editor.registerNodeTransform(DiffNode, (node) => {
|
|
43
60
|
if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
44
61
|
}));
|
|
@@ -63,11 +80,14 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
63
80
|
const diffNode = node;
|
|
64
81
|
const lines = [];
|
|
65
82
|
switch (diffNode.diffType) {
|
|
66
|
-
case "modify":
|
|
67
|
-
|
|
83
|
+
case "modify": {
|
|
84
|
+
const after = diffNode.getChildAtIndex(1);
|
|
85
|
+
if ($isDiffContentNode(after)) after.getChildren().forEach((child) => nodeToXML(child, lines, indent));
|
|
86
|
+
else nodeToXML(after, lines, indent);
|
|
68
87
|
break;
|
|
88
|
+
}
|
|
69
89
|
case "add":
|
|
70
|
-
|
|
90
|
+
diffNode.getChildren().forEach((child) => nodeToXML(child, lines, indent));
|
|
71
91
|
break;
|
|
72
92
|
case "remove": break;
|
|
73
93
|
case "listItemModify": diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
|
|
@@ -76,16 +96,49 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
76
96
|
}
|
|
77
97
|
return { lines };
|
|
78
98
|
});
|
|
99
|
+
this.service.registerXMLWriter(TableRowDiffNode.getType(), (node, ctx) => {
|
|
100
|
+
if (!$isTableRowDiffNode(node)) return false;
|
|
101
|
+
if (node.getDiffType() === "remove") return { lines: [] };
|
|
102
|
+
return ctx.createXmlNode("tr", {});
|
|
103
|
+
});
|
|
104
|
+
this.service.registerXMLWriter(TableCellDiffNode.getType(), (node, ctx) => {
|
|
105
|
+
if (!$isTableCellDiffNode(node)) return false;
|
|
106
|
+
if (node.getDiffType() === "remove") return { lines: [] };
|
|
107
|
+
const attributes = {};
|
|
108
|
+
if (node.getColSpan() > 1) attributes.colSpan = String(node.getColSpan());
|
|
109
|
+
if (node.getRowSpan() > 1) attributes.rowSpan = String(node.getRowSpan());
|
|
110
|
+
if (node.getBackgroundColor()) attributes.backgroundColor = node.getBackgroundColor();
|
|
111
|
+
return ctx.createXmlNode("td", attributes);
|
|
112
|
+
});
|
|
79
113
|
}
|
|
80
114
|
registerMarkdown() {
|
|
81
|
-
this.kernel.requireService(IMarkdownShortCutService)
|
|
115
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
116
|
+
markdownService?.registerMarkdownWriter(TableCellDiffNode.getType(), (ctx, node) => {
|
|
117
|
+
if (!$isTableCellDiffNode(node)) return false;
|
|
118
|
+
if (node.getDiffType() === "remove") return true;
|
|
119
|
+
if (!node.getNextSibling()) ctx.wrap("|", "|");
|
|
120
|
+
else ctx.wrap("|", "");
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
markdownService?.registerMarkdownWriter(TableRowDiffNode.getType(), (ctx, node) => {
|
|
124
|
+
if (!$isTableRowDiffNode(node)) return false;
|
|
125
|
+
if (node.getDiffType() === "remove") return true;
|
|
126
|
+
const parent = node.getParent();
|
|
127
|
+
if (!$isTableNode(parent)) return true;
|
|
128
|
+
if (node.getPreviousSiblings().some((sibling) => !$isTableRowDiffNode(sibling) || sibling.getDiffType() !== "remove")) ctx.wrap("", "\n");
|
|
129
|
+
else ctx.wrap("", `\n${parent.getColWidths()?.map(() => "|:--").join("")}|\n`);
|
|
130
|
+
});
|
|
131
|
+
markdownService?.registerMarkdownWriter(DiffNode.getType(), (ctx, node) => {
|
|
82
132
|
const diffNode = node;
|
|
83
133
|
switch (diffNode.diffType) {
|
|
84
|
-
case "modify":
|
|
85
|
-
|
|
134
|
+
case "modify": {
|
|
135
|
+
const after = diffNode.getChildAtIndex(1);
|
|
136
|
+
if ($isDiffContentNode(after)) after.getChildren().forEach((child) => ctx.processChild(ctx, child));
|
|
137
|
+
else ctx.processChild(ctx, after);
|
|
86
138
|
break;
|
|
139
|
+
}
|
|
87
140
|
case "add":
|
|
88
|
-
ctx.processChild(ctx,
|
|
141
|
+
diffNode.getChildren().forEach((child) => ctx.processChild(ctx, child));
|
|
89
142
|
break;
|
|
90
143
|
case "remove": break;
|
|
91
144
|
case "listItemModify": diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
|
|
@@ -10,6 +10,14 @@ import { useThemeMode } from "antd-style";
|
|
|
10
10
|
const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
11
11
|
const t = useTranslation();
|
|
12
12
|
const { isDarkMode } = useThemeMode();
|
|
13
|
+
const handleActionPointerDown = (event, action) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
event.stopPropagation();
|
|
16
|
+
editor.dispatchCommand(LITEXML_DIFFNODE_COMMAND, {
|
|
17
|
+
action,
|
|
18
|
+
nodeKey: node.getKey()
|
|
19
|
+
});
|
|
20
|
+
};
|
|
13
21
|
return /* @__PURE__ */ jsx(LexicalPortalContainer, {
|
|
14
22
|
editor,
|
|
15
23
|
node,
|
|
@@ -25,12 +33,7 @@ const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
|
25
33
|
className: styles.reject,
|
|
26
34
|
danger: true,
|
|
27
35
|
icon: X,
|
|
28
|
-
|
|
29
|
-
editor.dispatchCommand(LITEXML_DIFFNODE_COMMAND, {
|
|
30
|
-
action: 0,
|
|
31
|
-
nodeKey: node.getKey()
|
|
32
|
-
});
|
|
33
|
-
},
|
|
36
|
+
onPointerDown: (event) => handleActionPointerDown(event, 0),
|
|
34
37
|
size: {
|
|
35
38
|
blockSize: 28,
|
|
36
39
|
size: 16
|
|
@@ -40,12 +43,7 @@ const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
|
40
43
|
"aria-label": "Accept change",
|
|
41
44
|
className: styles.accept,
|
|
42
45
|
icon: Check,
|
|
43
|
-
|
|
44
|
-
editor.dispatchCommand(LITEXML_DIFFNODE_COMMAND, {
|
|
45
|
-
action: 1,
|
|
46
|
-
nodeKey: node.getKey()
|
|
47
|
-
});
|
|
48
|
-
},
|
|
46
|
+
onPointerDown: (event) => handleActionPointerDown(event, 1),
|
|
49
47
|
size: {
|
|
50
48
|
blockSize: 28,
|
|
51
49
|
size: 16
|