@kerebron/extension-basic-editor 0.1.2 → 0.1.3
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/esm/extension-basic-editor/src/NodeImage.d.ts +1 -19
- package/esm/extension-basic-editor/src/NodeImage.d.ts.map +1 -1
- package/esm/extension-basic-editor/src/NodeImage.js +0 -22
- package/package.json +1 -4
- package/esm/extension-automerge/src/CursorPlugin.d.ts +0 -16
- package/esm/extension-automerge/src/CursorPlugin.d.ts.map +0 -1
- package/esm/extension-automerge/src/CursorPlugin.js +0 -114
- package/esm/extension-automerge/src/ExtensionAutomerge.d.ts +0 -26
- package/esm/extension-automerge/src/ExtensionAutomerge.d.ts.map +0 -1
- package/esm/extension-automerge/src/ExtensionAutomerge.js +0 -136
- package/esm/extension-automerge/src/MarkUnknown.d.ts +0 -8
- package/esm/extension-automerge/src/MarkUnknown.d.ts.map +0 -1
- package/esm/extension-automerge/src/MarkUnknown.js +0 -26
- package/esm/extension-automerge/src/NodeUnknown.d.ts +0 -8
- package/esm/extension-automerge/src/NodeUnknown.d.ts.map +0 -1
- package/esm/extension-automerge/src/NodeUnknown.js +0 -28
- package/esm/extension-automerge/src/NodeUnknownBlock.d.ts +0 -11
- package/esm/extension-automerge/src/NodeUnknownBlock.d.ts.map +0 -1
- package/esm/extension-automerge/src/NodeUnknownBlock.js +0 -36
- package/esm/extension-automerge/src/SchemaAdapter.d.ts +0 -70
- package/esm/extension-automerge/src/SchemaAdapter.d.ts.map +0 -1
- package/esm/extension-automerge/src/SchemaAdapter.js +0 -188
- package/esm/extension-automerge/src/SyncPlugin.d.ts +0 -16
- package/esm/extension-automerge/src/SyncPlugin.d.ts.map +0 -1
- package/esm/extension-automerge/src/SyncPlugin.js +0 -147
- package/esm/extension-automerge/src/amToPm.d.ts +0 -9
- package/esm/extension-automerge/src/amToPm.d.ts.map +0 -1
- package/esm/extension-automerge/src/amToPm.js +0 -245
- package/esm/extension-automerge/src/amTraversal.d.ts +0 -62
- package/esm/extension-automerge/src/amTraversal.d.ts.map +0 -1
- package/esm/extension-automerge/src/amTraversal.js +0 -688
- package/esm/extension-automerge/src/loader.d.ts +0 -8
- package/esm/extension-automerge/src/loader.d.ts.map +0 -1
- package/esm/extension-automerge/src/loader.js +0 -54
- package/esm/extension-automerge/src/maintainSpans.d.ts +0 -9
- package/esm/extension-automerge/src/maintainSpans.d.ts.map +0 -1
- package/esm/extension-automerge/src/maintainSpans.js +0 -464
- package/esm/extension-automerge/src/pmToAm.d.ts +0 -6
- package/esm/extension-automerge/src/pmToAm.d.ts.map +0 -1
- package/esm/extension-automerge/src/pmToAm.js +0 -183
- package/esm/extension-automerge/src/pmTraversal.d.ts +0 -26
- package/esm/extension-automerge/src/pmTraversal.d.ts.map +0 -1
- package/esm/extension-automerge/src/pmTraversal.js +0 -102
- package/esm/extension-automerge/src/types.d.ts +0 -42
- package/esm/extension-automerge/src/types.d.ts.map +0 -1
- package/esm/extension-automerge/src/types.js +0 -94
- package/esm/extension-automerge/src/utils.d.ts +0 -3
- package/esm/extension-automerge/src/utils.d.ts.map +0 -1
- package/esm/extension-automerge/src/utils.js +0 -18
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
export class SchemaAdapter {
|
|
2
|
-
constructor(schema) {
|
|
3
|
-
Object.defineProperty(this, "nodeMappings", {
|
|
4
|
-
enumerable: true,
|
|
5
|
-
configurable: true,
|
|
6
|
-
writable: true,
|
|
7
|
-
value: void 0
|
|
8
|
-
});
|
|
9
|
-
Object.defineProperty(this, "markMappings", {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
configurable: true,
|
|
12
|
-
writable: true,
|
|
13
|
-
value: void 0
|
|
14
|
-
});
|
|
15
|
-
Object.defineProperty(this, "unknownBlock", {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
configurable: true,
|
|
18
|
-
writable: true,
|
|
19
|
-
value: void 0
|
|
20
|
-
});
|
|
21
|
-
Object.defineProperty(this, "unknownLeaf", {
|
|
22
|
-
enumerable: true,
|
|
23
|
-
configurable: true,
|
|
24
|
-
writable: true,
|
|
25
|
-
value: void 0
|
|
26
|
-
});
|
|
27
|
-
Object.defineProperty(this, "unknownMark", {
|
|
28
|
-
enumerable: true,
|
|
29
|
-
configurable: true,
|
|
30
|
-
writable: true,
|
|
31
|
-
value: void 0
|
|
32
|
-
});
|
|
33
|
-
Object.defineProperty(this, "schema", {
|
|
34
|
-
enumerable: true,
|
|
35
|
-
configurable: true,
|
|
36
|
-
writable: true,
|
|
37
|
-
value: void 0
|
|
38
|
-
});
|
|
39
|
-
const nodeMappings = [];
|
|
40
|
-
const markMappings = [];
|
|
41
|
-
let unknownBlock = null;
|
|
42
|
-
for (const [nodeName, nodeSpec] of Object.entries(schema.nodes)) {
|
|
43
|
-
const adaptSpec = nodeSpec.spec.automerge;
|
|
44
|
-
if (adaptSpec == null) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (adaptSpec.unknownBlock) {
|
|
48
|
-
if (unknownBlock != null) {
|
|
49
|
-
throw new Error('only one node can be marked as unknownBlock');
|
|
50
|
-
}
|
|
51
|
-
unknownBlock = schema.nodes[nodeName];
|
|
52
|
-
}
|
|
53
|
-
if (adaptSpec.block != null) {
|
|
54
|
-
const nodeMapping = {
|
|
55
|
-
blockName: adaptSpec.block,
|
|
56
|
-
content: schema.nodes[nodeName],
|
|
57
|
-
isEmbed: adaptSpec.isEmbed || false,
|
|
58
|
-
};
|
|
59
|
-
if (adaptSpec.attrParsers != null) {
|
|
60
|
-
nodeMapping.attrParsers = adaptSpec.attrParsers;
|
|
61
|
-
}
|
|
62
|
-
nodeMappings.push(nodeMapping);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
for (const [markName, markSpec] of Object.entries(schema.marks || {})) {
|
|
66
|
-
const adaptSpec = markSpec.spec.automerge;
|
|
67
|
-
if (adaptSpec == null) {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (adaptSpec.markName != null) {
|
|
71
|
-
let parsers;
|
|
72
|
-
if (adaptSpec.parsers != null) {
|
|
73
|
-
parsers = adaptSpec.parsers;
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
parsers = {
|
|
77
|
-
fromAutomerge: () => ({}),
|
|
78
|
-
fromProsemirror: () => true,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
markMappings.push({
|
|
82
|
-
automergeMarkName: adaptSpec.markName,
|
|
83
|
-
prosemirrorMark: schema.marks[markName],
|
|
84
|
-
parsers,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (unknownBlock == null) {
|
|
89
|
-
throw new Error(`no unknown block specified: one node must be marked as the unknownblock
|
|
90
|
-
by setting the automerge.unknownBlock property to true`);
|
|
91
|
-
}
|
|
92
|
-
this.unknownMark = schema.marks.unknownMark;
|
|
93
|
-
this.nodeMappings = nodeMappings;
|
|
94
|
-
this.markMappings = markMappings;
|
|
95
|
-
this.unknownLeaf = schema.nodes.unknownLeaf;
|
|
96
|
-
this.unknownBlock = unknownBlock;
|
|
97
|
-
this.schema = schema;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
function shallowClone(spec) {
|
|
101
|
-
const nodes = {};
|
|
102
|
-
for (const [nodeName, node] of Object.entries(spec.nodes)) {
|
|
103
|
-
const shallowCopy = Object.assign({}, node);
|
|
104
|
-
if (node.attrs != null) {
|
|
105
|
-
shallowCopy.attrs = Object.assign({}, node.attrs);
|
|
106
|
-
}
|
|
107
|
-
nodes[nodeName] = shallowCopy;
|
|
108
|
-
}
|
|
109
|
-
const marks = {};
|
|
110
|
-
if (spec.marks != null) {
|
|
111
|
-
for (const [markName, mark] of Object.entries(spec.marks)) {
|
|
112
|
-
const shallowCopy = Object.assign({}, mark);
|
|
113
|
-
if (mark.attrs != null) {
|
|
114
|
-
shallowCopy.attrs = Object.assign({}, mark.attrs);
|
|
115
|
-
}
|
|
116
|
-
marks[markName] = shallowCopy;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return { nodes, marks };
|
|
120
|
-
}
|
|
121
|
-
export function addAmgNodeStateAttrs(nodes) {
|
|
122
|
-
for (const [name, nodeSpec] of Object.entries(nodes)) {
|
|
123
|
-
if (name !== 'text') {
|
|
124
|
-
if (nodeSpec.attrs == null) {
|
|
125
|
-
nodeSpec.attrs = {
|
|
126
|
-
isAmgBlock: { default: false },
|
|
127
|
-
unknownAttrs: { default: null },
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
nodeSpec.attrs.isAmgBlock = { default: false };
|
|
132
|
-
nodeSpec.attrs.unknownAttrs = { default: null };
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (nodeSpec.automerge?.unknownBlock) {
|
|
136
|
-
if (nodeSpec.attrs == null) {
|
|
137
|
-
nodeSpec.attrs = {
|
|
138
|
-
unknownParentBlock: { default: null },
|
|
139
|
-
unknownBlock: { default: null },
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
nodeSpec.attrs.unknownParentBlock = { default: null };
|
|
144
|
-
nodeSpec.attrs.unknownBlock = { default: null };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return nodes;
|
|
149
|
-
}
|
|
150
|
-
export function amMarksFromPmMarks(adapter, marks) {
|
|
151
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
-
const result = {};
|
|
153
|
-
marks.forEach((mark) => {
|
|
154
|
-
const markMapping = adapter.markMappings.find((m) => m.prosemirrorMark === mark.type);
|
|
155
|
-
if (markMapping != null) {
|
|
156
|
-
result[markMapping.automergeMarkName] = markMapping.parsers
|
|
157
|
-
.fromProsemirror(mark);
|
|
158
|
-
}
|
|
159
|
-
else if (mark.type === adapter.unknownMark) {
|
|
160
|
-
for (const [key, value] of Object.entries(mark.attrs.unknownMarks)) {
|
|
161
|
-
result[key] = value;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
return result;
|
|
166
|
-
}
|
|
167
|
-
export function pmMarksFromAmMarks(adapter, amMarks) {
|
|
168
|
-
const unknownMarks = {};
|
|
169
|
-
let hasUnknownMark = false;
|
|
170
|
-
const pmMarks = [];
|
|
171
|
-
for (const [markName, markValue] of Object.entries(amMarks)) {
|
|
172
|
-
// Filter tombstoned marks (https://github.com/automerge/automerge/issues/715).
|
|
173
|
-
if (markValue == null)
|
|
174
|
-
continue;
|
|
175
|
-
const mapping = adapter.markMappings.find((m) => m.automergeMarkName === markName);
|
|
176
|
-
if (mapping == null) {
|
|
177
|
-
unknownMarks[markName] = markValue;
|
|
178
|
-
hasUnknownMark = true;
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
pmMarks.push(mapping.prosemirrorMark.create(mapping.parsers.fromAutomerge(markValue)));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (hasUnknownMark) {
|
|
185
|
-
pmMarks.push(adapter.unknownMark.create({ unknownMarks }));
|
|
186
|
-
}
|
|
187
|
-
return pmMarks;
|
|
188
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Plugin, PluginKey } from 'prosemirror-state';
|
|
2
|
-
import { next as automerge } from '@automerge/automerge/slim';
|
|
3
|
-
import { SchemaAdapter } from './SchemaAdapter.js';
|
|
4
|
-
import { DocHandle, DocHandleChangePayload } from './types.js';
|
|
5
|
-
export declare const syncPluginKey: PluginKey<any>;
|
|
6
|
-
export declare class SyncPlugin<T> extends Plugin {
|
|
7
|
-
private adapter;
|
|
8
|
-
private path;
|
|
9
|
-
ignoreTr: boolean;
|
|
10
|
-
onAutoMergeChange: (args: DocHandleChangePayload<unknown>) => void;
|
|
11
|
-
private handle;
|
|
12
|
-
private view;
|
|
13
|
-
constructor(adapter: SchemaAdapter, path: automerge.Prop[], handle: DocHandle<T>);
|
|
14
|
-
changeHandle(handle: DocHandle<any>): void;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=SyncPlugin.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SyncPlugin.d.ts","sourceRoot":"","sources":["../../../src/extension-automerge/src/SyncPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAa,MAAM,mBAAmB,CAAC;AAGjE,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI9D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAG/D,eAAO,MAAM,aAAa,gBAAkC,CAAC;AAE7D,qBAAa,UAAU,CAAC,CAAC,CAAE,SAAQ,MAAM;IAQrC,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,IAAI;IARd,QAAQ,UAAS;IACjB,iBAAiB,EAAG,CAAC,IAAI,EAAE,sBAAsB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAEpE,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,IAAI,CAAc;gBAGhB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,EAC9B,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAiGtB,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC;CAwCpC"}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { Plugin, PluginKey, Selection } from 'prosemirror-state';
|
|
2
|
-
import { ChangeSet } from 'prosemirror-changeset';
|
|
3
|
-
import { next as automerge } from '@automerge/automerge/slim';
|
|
4
|
-
import { pmToAm } from './pmToAm.js';
|
|
5
|
-
import { amToPm } from './amToPm.js';
|
|
6
|
-
import { amSpansToDoc } from './amTraversal.js';
|
|
7
|
-
import { isArrayEqual } from './utils.js';
|
|
8
|
-
export const syncPluginKey = new PluginKey('automerge-sync');
|
|
9
|
-
export class SyncPlugin extends Plugin {
|
|
10
|
-
constructor(adapter, path, handle) {
|
|
11
|
-
super({
|
|
12
|
-
key: syncPluginKey,
|
|
13
|
-
view: (view) => {
|
|
14
|
-
this.view = view;
|
|
15
|
-
this.changeHandle(handle);
|
|
16
|
-
return {
|
|
17
|
-
destroy: () => {
|
|
18
|
-
if (this.handle) {
|
|
19
|
-
this.handle.off('change', this.onAutoMergeChange);
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
},
|
|
24
|
-
appendTransaction(transactions, oldState, state) {
|
|
25
|
-
if (this.ignoreTr)
|
|
26
|
-
return;
|
|
27
|
-
transactions = transactions.filter((tx) => tx.docChanged);
|
|
28
|
-
if (transactions.length === 0)
|
|
29
|
-
return undefined;
|
|
30
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
31
|
-
const docBefore = this.handle.docSync();
|
|
32
|
-
const headsBefore = automerge.getHeads(docBefore);
|
|
33
|
-
const spansBefore = automerge.spans(docBefore, path);
|
|
34
|
-
// Apply transactions to the automerge doc
|
|
35
|
-
this.ignoreTr = true;
|
|
36
|
-
this.handle.change((doc) => {
|
|
37
|
-
for (const tx of transactions) {
|
|
38
|
-
const spans = automerge.spans(doc, path);
|
|
39
|
-
pmToAm(adapter, spans, tx.steps, doc, tx.docs[0], path);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
this.ignoreTr = false;
|
|
43
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
44
|
-
const docAfter = this.handle.docSync();
|
|
45
|
-
const headsAfter = automerge.getHeads(docAfter);
|
|
46
|
-
const spansAfter = automerge.spans(docAfter, path);
|
|
47
|
-
// Ignore if nothing changed.
|
|
48
|
-
if (isArrayEqual(headsBefore, headsAfter))
|
|
49
|
-
return undefined;
|
|
50
|
-
// Check if ProseMirror doc matches the AutoMerge doc
|
|
51
|
-
// by comparing changesets between the two transactions.
|
|
52
|
-
const patches = automerge.diff(docAfter, headsBefore, headsAfter);
|
|
53
|
-
const tx = amToPm(adapter, spansBefore, patches, path, oldState.tr);
|
|
54
|
-
let amChangeSet = ChangeSet.create(oldState.doc);
|
|
55
|
-
amChangeSet = amChangeSet.addSteps(oldState.doc, tx.mapping.maps, undefined);
|
|
56
|
-
let pmChangeSet = ChangeSet.create(oldState.doc);
|
|
57
|
-
for (const tr of transactions) {
|
|
58
|
-
pmChangeSet = pmChangeSet.addSteps(tr.docs[0], tr.mapping.maps, undefined);
|
|
59
|
-
}
|
|
60
|
-
const diff = pmChangeSet.changedRange(amChangeSet);
|
|
61
|
-
if (!diff || diff.from === diff.to)
|
|
62
|
-
return undefined;
|
|
63
|
-
console.warn("Warning: ProseMirror doc doesn't match AutoMerge spans.\n\n" +
|
|
64
|
-
'State will be automatically fixed with a tr. File an issue at https://github.com/automerge/automerge-repo.\n', {
|
|
65
|
-
spansBefore,
|
|
66
|
-
steps: transactions.map((tr) => tr.steps.map((s) => s.toJSON())),
|
|
67
|
-
});
|
|
68
|
-
// Replace the diff range in ProseMirror doc from the AutoMerge doc.
|
|
69
|
-
const doc = amSpansToDoc(adapter, spansAfter);
|
|
70
|
-
const slice = doc.slice(diff.from, diff.to);
|
|
71
|
-
const tr = state.tr;
|
|
72
|
-
tr.replace(diff.from, diff.to, slice);
|
|
73
|
-
try {
|
|
74
|
-
tr.setSelection(Selection.fromJSON(tr.doc, state.selection.toJSON()));
|
|
75
|
-
}
|
|
76
|
-
catch (e) {
|
|
77
|
-
if (e instanceof RangeError) {
|
|
78
|
-
// Sometimes the selection can't be mapped for some reason, so we just give up and hope for the best
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
throw e;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
tr.setStoredMarks(state.storedMarks);
|
|
85
|
-
return tr;
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
Object.defineProperty(this, "adapter", {
|
|
89
|
-
enumerable: true,
|
|
90
|
-
configurable: true,
|
|
91
|
-
writable: true,
|
|
92
|
-
value: adapter
|
|
93
|
-
});
|
|
94
|
-
Object.defineProperty(this, "path", {
|
|
95
|
-
enumerable: true,
|
|
96
|
-
configurable: true,
|
|
97
|
-
writable: true,
|
|
98
|
-
value: path
|
|
99
|
-
});
|
|
100
|
-
Object.defineProperty(this, "ignoreTr", {
|
|
101
|
-
enumerable: true,
|
|
102
|
-
configurable: true,
|
|
103
|
-
writable: true,
|
|
104
|
-
value: false
|
|
105
|
-
});
|
|
106
|
-
Object.defineProperty(this, "onAutoMergeChange", {
|
|
107
|
-
enumerable: true,
|
|
108
|
-
configurable: true,
|
|
109
|
-
writable: true,
|
|
110
|
-
value: void 0
|
|
111
|
-
});
|
|
112
|
-
Object.defineProperty(this, "handle", {
|
|
113
|
-
enumerable: true,
|
|
114
|
-
configurable: true,
|
|
115
|
-
writable: true,
|
|
116
|
-
value: void 0
|
|
117
|
-
});
|
|
118
|
-
Object.defineProperty(this, "view", {
|
|
119
|
-
enumerable: true,
|
|
120
|
-
configurable: true,
|
|
121
|
-
writable: true,
|
|
122
|
-
value: void 0
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
changeHandle(handle) {
|
|
126
|
-
if (!this.view) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (this.handle) {
|
|
130
|
-
this.handle.off('change', this.onAutoMergeChange);
|
|
131
|
-
}
|
|
132
|
-
const onAutoMergeChange = ({ doc, patches, patchInfo, }) => {
|
|
133
|
-
if (this.ignoreTr)
|
|
134
|
-
return;
|
|
135
|
-
const headsBefore = automerge.getHeads(patchInfo.before);
|
|
136
|
-
const spans = automerge.spans(automerge.view(doc, headsBefore), this.path);
|
|
137
|
-
const tr = amToPm(this.adapter, spans, patches, this.path, this.view.state.tr);
|
|
138
|
-
tr.setMeta('addToHistory', false); // remote changes should not be added to local stack
|
|
139
|
-
this.ignoreTr = true;
|
|
140
|
-
this.view.dispatch(tr);
|
|
141
|
-
this.ignoreTr = false;
|
|
142
|
-
};
|
|
143
|
-
this.onAutoMergeChange = onAutoMergeChange;
|
|
144
|
-
handle.on('change', this.onAutoMergeChange);
|
|
145
|
-
this.handle = handle;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { next as automerge, Patch, type Prop } from '@automerge/automerge/slim';
|
|
2
|
-
import { Transaction } from 'prosemirror-state';
|
|
3
|
-
import { SchemaAdapter } from './SchemaAdapter.js';
|
|
4
|
-
type SpliceTextPatch = automerge.SpliceTextPatch;
|
|
5
|
-
export declare function amToPm(adapter: SchemaAdapter, spansAtStart: automerge.Span[], patches: Array<Patch>, path: Prop[], tx: Transaction): Transaction;
|
|
6
|
-
export declare function handleSplice(adapter: SchemaAdapter, spans: automerge.Span[], patch: SpliceTextPatch, path: Prop[], tx: Transaction): Transaction;
|
|
7
|
-
export declare function handleBlockChange(adapter: SchemaAdapter, atPath: automerge.Prop[], spans: automerge.Span[], _blockIdx: number, patches: automerge.Patch[], tx: Transaction): Transaction;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=amToPm.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"amToPm.d.ts","sourceRoot":"","sources":["../../../src/extension-automerge/src/amToPm.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,IAAI,SAAS,EACjB,KAAK,EACL,KAAK,IAAI,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMhD,OAAO,EAAsB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEvE,KAAK,eAAe,GAAG,SAAS,CAAC,eAAe,CAAC;AAQjD,wBAAgB,MAAM,CACpB,OAAO,EAAE,aAAa,EACtB,YAAY,EAAE,SAAS,CAAC,IAAI,EAAE,EAC9B,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EACrB,IAAI,EAAE,IAAI,EAAE,EACZ,EAAE,EAAE,WAAW,GACd,WAAW,CA0Cb;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,EACvB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,IAAI,EAAE,EACZ,EAAE,EAAE,WAAW,GACd,WAAW,CAQb;AAkDD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,EACxB,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE,EAC1B,EAAE,EAAE,WAAW,GACd,WAAW,CA0Cb"}
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { Fragment, Slice } from 'prosemirror-model';
|
|
2
|
-
import { ReplaceStep } from 'prosemirror-transform';
|
|
3
|
-
import { amSpansToDoc, amSpliceIdxToPmIdx } from './amTraversal.js';
|
|
4
|
-
import { findBlockAtCharIdx, patchSpans } from './maintainSpans.js';
|
|
5
|
-
import { isArrayEqual, isPrefixOfArray } from './utils.js';
|
|
6
|
-
import { pmMarksFromAmMarks } from './SchemaAdapter.js';
|
|
7
|
-
export function amToPm(adapter, spansAtStart, patches, path, tx) {
|
|
8
|
-
const gathered = gatherPatches(path, patches);
|
|
9
|
-
for (const patchGroup of gathered) {
|
|
10
|
-
if (patchGroup.type === 'text') {
|
|
11
|
-
for (const patch of patchGroup.patches) {
|
|
12
|
-
if (patch.action === 'splice') {
|
|
13
|
-
tx = handleSplice(adapter, spansAtStart, patch, path, tx);
|
|
14
|
-
patchSpans(path, spansAtStart, patch);
|
|
15
|
-
}
|
|
16
|
-
else if (patch.action === 'del') {
|
|
17
|
-
const patchIndex = patch.path[patch.path.length - 1];
|
|
18
|
-
const block = findBlockAtCharIdx(spansAtStart, patchIndex);
|
|
19
|
-
if (block != null) {
|
|
20
|
-
tx = handleBlockChange(adapter, path, spansAtStart, patchIndex, [patch], tx);
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
tx = handleDelete(adapter, spansAtStart, patch, path, tx);
|
|
24
|
-
}
|
|
25
|
-
patchSpans(path, spansAtStart, patch);
|
|
26
|
-
}
|
|
27
|
-
else if (patch.action === 'mark') {
|
|
28
|
-
tx = handleMark(adapter, spansAtStart, patch, path, tx);
|
|
29
|
-
patchSpans(path, spansAtStart, patch);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
tx = handleBlockChange(adapter, path, spansAtStart, patchGroup.index, patchGroup.patches, tx);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return tx;
|
|
38
|
-
}
|
|
39
|
-
export function handleSplice(adapter, spans, patch, path, tx) {
|
|
40
|
-
const index = charPath(path, patch.path);
|
|
41
|
-
if (index === null)
|
|
42
|
-
return tx;
|
|
43
|
-
const pmIdx = amSpliceIdxToPmIdx(adapter, spans, index);
|
|
44
|
-
if (pmIdx == null)
|
|
45
|
-
throw new Error('Invalid index');
|
|
46
|
-
const content = patchContentToFragment(adapter, patch.value, patch.marks);
|
|
47
|
-
tx = tx.step(new ReplaceStep(pmIdx, pmIdx, new Slice(content, 0, 0)));
|
|
48
|
-
return tx;
|
|
49
|
-
}
|
|
50
|
-
function handleDelete(adapter, spans, patch, path, tx) {
|
|
51
|
-
const index = charPath(path, patch.path);
|
|
52
|
-
if (index === null)
|
|
53
|
-
return tx;
|
|
54
|
-
const start = amSpliceIdxToPmIdx(adapter, spans, index);
|
|
55
|
-
if (start == null)
|
|
56
|
-
throw new Error('Invalid index');
|
|
57
|
-
const end = start + (patch.length || 1);
|
|
58
|
-
return tx.delete(start, end);
|
|
59
|
-
}
|
|
60
|
-
function handleMark(adapter, spans, patch, path, tx) {
|
|
61
|
-
if (isArrayEqual(patch.path, path)) {
|
|
62
|
-
for (const mark of patch.marks) {
|
|
63
|
-
const pmStart = amSpliceIdxToPmIdx(adapter, spans, mark.start);
|
|
64
|
-
const pmEnd = amSpliceIdxToPmIdx(adapter, spans, mark.end);
|
|
65
|
-
if (pmStart == null || pmEnd == null)
|
|
66
|
-
throw new Error('Invalid index');
|
|
67
|
-
if (mark.value == null) {
|
|
68
|
-
const markMapping = adapter.markMappings.find((m) => m.automergeMarkName === mark.name);
|
|
69
|
-
const markType = markMapping
|
|
70
|
-
? markMapping.prosemirrorMark
|
|
71
|
-
: adapter.unknownMark;
|
|
72
|
-
tx = tx.removeMark(pmStart, pmEnd, markType);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
const pmMarks = pmMarksFromAmMarks(adapter, {
|
|
76
|
-
[mark.name]: mark.value,
|
|
77
|
-
});
|
|
78
|
-
for (const pmMark of pmMarks) {
|
|
79
|
-
tx = tx.addMark(pmStart, pmEnd, pmMark);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return tx;
|
|
85
|
-
}
|
|
86
|
-
export function handleBlockChange(adapter, atPath, spans, _blockIdx, patches, tx) {
|
|
87
|
-
for (const patch of patches) {
|
|
88
|
-
patchSpans(atPath, spans, patch);
|
|
89
|
-
}
|
|
90
|
-
const docAfter = amSpansToDoc(adapter, spans);
|
|
91
|
-
const change = findDiff(tx.doc.content, docAfter.content);
|
|
92
|
-
if (change == null)
|
|
93
|
-
return tx;
|
|
94
|
-
const $from = docAfter.resolve(change.start);
|
|
95
|
-
const $to = docAfter.resolve(change.endB);
|
|
96
|
-
const $fromA = tx.doc.resolve(change.start);
|
|
97
|
-
const inlineChange = $from.sameParent($to) &&
|
|
98
|
-
$from.parent.inlineContent &&
|
|
99
|
-
$fromA.end() >= change.endA;
|
|
100
|
-
const chFrom = change.start;
|
|
101
|
-
const chTo = change.endA;
|
|
102
|
-
let handledByInline = false;
|
|
103
|
-
if (inlineChange) {
|
|
104
|
-
if ($from.pos == $to.pos) {
|
|
105
|
-
// Deletion
|
|
106
|
-
handledByInline = true;
|
|
107
|
-
tx = tx.delete(chFrom, chTo);
|
|
108
|
-
}
|
|
109
|
-
else if ($from.parent.child($from.index()).isText &&
|
|
110
|
-
$from.index() == $to.index() - ($to.textOffset ? 0 : 1)) {
|
|
111
|
-
handledByInline = true;
|
|
112
|
-
// Both positions in the same text node -- simply insert text
|
|
113
|
-
const text = $from.parent.textBetween($from.parentOffset, $to.parentOffset);
|
|
114
|
-
tx = tx.insertText(text, chFrom, chTo);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (!handledByInline) {
|
|
118
|
-
tx = tx.replace(chFrom, chTo, docAfter.slice(change.start, change.endB));
|
|
119
|
-
}
|
|
120
|
-
return tx;
|
|
121
|
-
}
|
|
122
|
-
function findDiff(a, b) {
|
|
123
|
-
let start = a.findDiffStart(b);
|
|
124
|
-
if (start == null)
|
|
125
|
-
return null;
|
|
126
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
127
|
-
let { a: endA, b: endB } = a.findDiffEnd(b);
|
|
128
|
-
if (endA < start && a.size < b.size) {
|
|
129
|
-
if (start &&
|
|
130
|
-
start < b.size &&
|
|
131
|
-
isSurrogatePair(b.textBetween(start - 1, start + 1))) {
|
|
132
|
-
start -= 1;
|
|
133
|
-
}
|
|
134
|
-
endB = start + (endB - endA);
|
|
135
|
-
endA = start;
|
|
136
|
-
}
|
|
137
|
-
else if (endB < start) {
|
|
138
|
-
if (start &&
|
|
139
|
-
start < a.size &&
|
|
140
|
-
isSurrogatePair(a.textBetween(start - 1, start + 1))) {
|
|
141
|
-
start -= 1;
|
|
142
|
-
}
|
|
143
|
-
endA = start + (endA - endB);
|
|
144
|
-
endB = start;
|
|
145
|
-
}
|
|
146
|
-
return { start, endA, endB };
|
|
147
|
-
}
|
|
148
|
-
function isSurrogatePair(str) {
|
|
149
|
-
if (str.length != 2)
|
|
150
|
-
return false;
|
|
151
|
-
const a = str.charCodeAt(0), b = str.charCodeAt(1);
|
|
152
|
-
return a >= 0xdc00 && a <= 0xdfff && b >= 0xd800 && b <= 0xdbff;
|
|
153
|
-
}
|
|
154
|
-
// If the path of the patch is of the form [path, <index>] then we know this is
|
|
155
|
-
// a path to a character within the sequence given by path
|
|
156
|
-
function charPath(textPath, candidatePath) {
|
|
157
|
-
if (candidatePath.length !== textPath.length + 1)
|
|
158
|
-
return null;
|
|
159
|
-
for (let i = 0; i < textPath.length; i++) {
|
|
160
|
-
if (textPath[i] !== candidatePath[i])
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
const index = candidatePath[candidatePath.length - 1];
|
|
164
|
-
if (typeof index === 'number')
|
|
165
|
-
return index;
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
function patchContentToFragment(adapter, patchContent, marks) {
|
|
169
|
-
let pmMarks = undefined;
|
|
170
|
-
if (marks != null) {
|
|
171
|
-
pmMarks = pmMarksFromAmMarks(adapter, marks);
|
|
172
|
-
}
|
|
173
|
-
// Splice is only ever called once a block has already been created so we're
|
|
174
|
-
// only inserting text. This means we don't have to think about openStart
|
|
175
|
-
// and openEnd
|
|
176
|
-
return Fragment.from(adapter.schema.text(patchContent, pmMarks));
|
|
177
|
-
}
|
|
178
|
-
function gatherPatches(textPath, diff) {
|
|
179
|
-
const result = [];
|
|
180
|
-
let state = { type: 'gatheringText', gathered: [] };
|
|
181
|
-
function flush() {
|
|
182
|
-
if (state.type === 'gatheringBlock') {
|
|
183
|
-
result.push({
|
|
184
|
-
type: 'block',
|
|
185
|
-
index: state.index,
|
|
186
|
-
patches: state.gathered,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
else if (state.type === 'gatheringText') {
|
|
190
|
-
result.push({ type: 'text', patches: state.gathered });
|
|
191
|
-
}
|
|
192
|
-
state = { type: 'transitioning' };
|
|
193
|
-
}
|
|
194
|
-
for (const patch of diff) {
|
|
195
|
-
if (!isPrefixOfArray(textPath, patch.path)) {
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (isArrayEqual(textPath, patch.path) && patch.action === 'mark') {
|
|
199
|
-
if (state.type === 'gatheringText') {
|
|
200
|
-
state.gathered.push(patch);
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
flush();
|
|
204
|
-
state = { type: 'gatheringText', gathered: [patch] };
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
else if (patch.path.length === textPath.length + 1) {
|
|
208
|
-
const lastElem = patch.path[patch.path.length - 1];
|
|
209
|
-
if (typeof lastElem === 'number') {
|
|
210
|
-
if (patch.action === 'splice' || patch.action === 'del') {
|
|
211
|
-
if (state.type === 'gatheringText') {
|
|
212
|
-
state.gathered.push(patch);
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
flush();
|
|
216
|
-
state = { type: 'gatheringText', gathered: [patch] };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else if (patch.action === 'insert') {
|
|
220
|
-
flush();
|
|
221
|
-
state = {
|
|
222
|
-
type: 'gatheringBlock',
|
|
223
|
-
index: lastElem,
|
|
224
|
-
gathered: [patch],
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
const index = patch.path[textPath.length];
|
|
231
|
-
if (typeof index !== 'number') {
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
if (state.type === 'gatheringBlock' && state.index === index) {
|
|
235
|
-
state.gathered.push(patch);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
flush();
|
|
239
|
-
state = { type: 'gatheringBlock', index: index, gathered: [patch] };
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
flush();
|
|
244
|
-
return result;
|
|
245
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { next as automerge } from '@automerge/automerge/slim';
|
|
2
|
-
import { Node } from 'prosemirror-model';
|
|
3
|
-
import { BlockMarker } from './types.js';
|
|
4
|
-
import { SchemaAdapter } from './SchemaAdapter.js';
|
|
5
|
-
type RenderRole = 'explicit' | 'render-only';
|
|
6
|
-
export type TraversalEvent = {
|
|
7
|
-
type: 'openTag';
|
|
8
|
-
tag: string;
|
|
9
|
-
role: RenderRole;
|
|
10
|
-
attrs?: {
|
|
11
|
-
[key: string]: any;
|
|
12
|
-
};
|
|
13
|
-
} | {
|
|
14
|
-
type: 'closeTag';
|
|
15
|
-
tag: string;
|
|
16
|
-
role: RenderRole;
|
|
17
|
-
} | {
|
|
18
|
-
type: 'leafNode';
|
|
19
|
-
tag: string;
|
|
20
|
-
role: RenderRole;
|
|
21
|
-
} | {
|
|
22
|
-
type: 'text';
|
|
23
|
-
text: string;
|
|
24
|
-
marks: automerge.MarkSet;
|
|
25
|
-
} | {
|
|
26
|
-
type: 'block';
|
|
27
|
-
isUnknown?: boolean;
|
|
28
|
-
block: {
|
|
29
|
-
type: string;
|
|
30
|
-
parents: string[];
|
|
31
|
-
attrs: {
|
|
32
|
-
[key: string]: automerge.MaterializeValue;
|
|
33
|
-
};
|
|
34
|
-
isEmbed: boolean;
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Convert an array of AutoMerge spans into a ProseMirror doc.
|
|
39
|
-
* @param adapter
|
|
40
|
-
* @param spans
|
|
41
|
-
* @returns
|
|
42
|
-
*/
|
|
43
|
-
export declare function amSpansToDoc(adapter: SchemaAdapter, spans: automerge.Span[]): Node;
|
|
44
|
-
export declare function amSpliceIdxToPmIdx(adapter: SchemaAdapter, spans: automerge.Span[], target: number): number | null;
|
|
45
|
-
export declare function amIdxToPmBlockIdx(adapter: SchemaAdapter, spans: automerge.Span[], target: number): number | null;
|
|
46
|
-
type Indexes = {
|
|
47
|
-
amIdx: number;
|
|
48
|
-
pmIdx: number;
|
|
49
|
-
};
|
|
50
|
-
export declare function eventsWithIndexChanges(events: IterableIterator<TraversalEvent>): IterableIterator<{
|
|
51
|
-
event: TraversalEvent;
|
|
52
|
-
before: Indexes;
|
|
53
|
-
after: Indexes;
|
|
54
|
-
}>;
|
|
55
|
-
export declare function traverseNode(adapter: SchemaAdapter, node: Node): IterableIterator<TraversalEvent>;
|
|
56
|
-
export declare function traverseSpans(adapter: SchemaAdapter, amSpans: automerge.Span[]): IterableIterator<TraversalEvent>;
|
|
57
|
-
export declare function blockAtIdx(spans: automerge.Span[], target: number): {
|
|
58
|
-
index: number;
|
|
59
|
-
block: BlockMarker;
|
|
60
|
-
} | null;
|
|
61
|
-
export {};
|
|
62
|
-
//# sourceMappingURL=amTraversal.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"amTraversal.d.ts","sourceRoot":"","sources":["../../../src/extension-automerge/src/amTraversal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAGL,IAAI,EAGL,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAgB,WAAW,EAAiB,MAAM,YAAY,CAAC;AACtE,OAAO,EAIL,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,KAAK,UAAU,GAAG,UAAU,GAAG,aAAa,CAAC;AAO7C,MAAM,MAAM,cAAc,GACtB;IACA,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IAEjB,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAChC,GACC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAA;CAAE,GACxD;IACA,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,gBAAgB,CAAA;SAAE,CAAC;QACrD,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC;CACH,CAAC;AAEJ;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,GACtB,IAAI,CAyDN;AAqCD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,EACvB,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CAyBf;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,EACvB,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CA4Bf;AAED,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAiB,sBAAsB,CACrC,MAAM,EAAE,gBAAgB,CAAC,cAAc,CAAC,GACvC,gBAAgB,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC,CA2BD;AAED,wBAAiB,YAAY,CAC3B,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,IAAI,GACT,gBAAgB,CAAC,cAAc,CAAC,CA4ElC;AAwPD,wBAAiB,aAAa,CAC5B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,GACxB,gBAAgB,CAAC,cAAc,CAAC,CAkBlC;AAmOD,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,EACvB,MAAM,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CAoB9C"}
|