rte-builder 1.0.0 → 2.0.2
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 +219 -132
- package/dist/index.css +997 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +538 -2
- package/dist/index.d.ts +538 -2
- package/dist/index.js +1453 -2196
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1433 -2189
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -10
package/dist/index.mjs
CHANGED
|
@@ -39,2150 +39,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
));
|
|
40
40
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
41
41
|
|
|
42
|
-
// node_modules/@tiptap/core/dist/index.js
|
|
43
|
-
import { Plugin, PluginKey, TextSelection, Selection, AllSelection, NodeSelection, EditorState } from "@tiptap/pm/state";
|
|
44
|
-
import { EditorView } from "@tiptap/pm/view";
|
|
45
|
-
import { keymap } from "@tiptap/pm/keymap";
|
|
46
|
-
import { Schema, DOMSerializer, Fragment, Node as Node$1, DOMParser as DOMParser2, Slice } from "@tiptap/pm/model";
|
|
47
|
-
import { liftTarget, ReplaceStep, ReplaceAroundStep, joinPoint, Transform, canSplit, canJoin, findWrapping } from "@tiptap/pm/transform";
|
|
48
|
-
import { createParagraphNear as createParagraphNear$1, deleteSelection as deleteSelection$1, exitCode as exitCode$1, joinUp as joinUp$1, joinDown as joinDown$1, joinBackward as joinBackward$1, joinForward as joinForward$1, joinTextblockBackward as joinTextblockBackward$1, joinTextblockForward as joinTextblockForward$1, lift as lift$1, liftEmptyBlock as liftEmptyBlock$1, newlineInCode as newlineInCode$1, selectNodeBackward as selectNodeBackward$1, selectNodeForward as selectNodeForward$1, selectParentNode as selectParentNode$1, selectTextblockEnd as selectTextblockEnd$1, selectTextblockStart as selectTextblockStart$1, setBlockType, wrapIn as wrapIn$1 } from "@tiptap/pm/commands";
|
|
49
|
-
import { liftListItem as liftListItem$1, sinkListItem as sinkListItem$1, wrapInList as wrapInList$1 } from "@tiptap/pm/schema-list";
|
|
50
|
-
function createChainableState(config) {
|
|
51
|
-
const { state, transaction } = config;
|
|
52
|
-
let { selection } = transaction;
|
|
53
|
-
let { doc } = transaction;
|
|
54
|
-
let { storedMarks } = transaction;
|
|
55
|
-
return {
|
|
56
|
-
...state,
|
|
57
|
-
apply: state.apply.bind(state),
|
|
58
|
-
applyTransaction: state.applyTransaction.bind(state),
|
|
59
|
-
plugins: state.plugins,
|
|
60
|
-
schema: state.schema,
|
|
61
|
-
reconfigure: state.reconfigure.bind(state),
|
|
62
|
-
toJSON: state.toJSON.bind(state),
|
|
63
|
-
get storedMarks() {
|
|
64
|
-
return storedMarks;
|
|
65
|
-
},
|
|
66
|
-
get selection() {
|
|
67
|
-
return selection;
|
|
68
|
-
},
|
|
69
|
-
get doc() {
|
|
70
|
-
return doc;
|
|
71
|
-
},
|
|
72
|
-
get tr() {
|
|
73
|
-
selection = transaction.selection;
|
|
74
|
-
doc = transaction.doc;
|
|
75
|
-
storedMarks = transaction.storedMarks;
|
|
76
|
-
return transaction;
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
function getExtensionField(extension, field, context) {
|
|
81
|
-
if (extension.config[field] === void 0 && extension.parent) {
|
|
82
|
-
return getExtensionField(extension.parent, field, context);
|
|
83
|
-
}
|
|
84
|
-
if (typeof extension.config[field] === "function") {
|
|
85
|
-
const value = extension.config[field].bind({
|
|
86
|
-
...context,
|
|
87
|
-
parent: extension.parent ? getExtensionField(extension.parent, field, context) : null
|
|
88
|
-
});
|
|
89
|
-
return value;
|
|
90
|
-
}
|
|
91
|
-
return extension.config[field];
|
|
92
|
-
}
|
|
93
|
-
function splitExtensions(extensions) {
|
|
94
|
-
const baseExtensions = extensions.filter((extension) => extension.type === "extension");
|
|
95
|
-
const nodeExtensions = extensions.filter((extension) => extension.type === "node");
|
|
96
|
-
const markExtensions = extensions.filter((extension) => extension.type === "mark");
|
|
97
|
-
return {
|
|
98
|
-
baseExtensions,
|
|
99
|
-
nodeExtensions,
|
|
100
|
-
markExtensions
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function getNodeType(nameOrType, schema) {
|
|
104
|
-
if (typeof nameOrType === "string") {
|
|
105
|
-
if (!schema.nodes[nameOrType]) {
|
|
106
|
-
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
107
|
-
}
|
|
108
|
-
return schema.nodes[nameOrType];
|
|
109
|
-
}
|
|
110
|
-
return nameOrType;
|
|
111
|
-
}
|
|
112
|
-
function mergeAttributes(...objects) {
|
|
113
|
-
return objects.filter((item) => !!item).reduce((items, item) => {
|
|
114
|
-
const mergedAttributes = { ...items };
|
|
115
|
-
Object.entries(item).forEach(([key, value]) => {
|
|
116
|
-
const exists = mergedAttributes[key];
|
|
117
|
-
if (!exists) {
|
|
118
|
-
mergedAttributes[key] = value;
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (key === "class") {
|
|
122
|
-
const valueClasses = value ? String(value).split(" ") : [];
|
|
123
|
-
const existingClasses = mergedAttributes[key] ? mergedAttributes[key].split(" ") : [];
|
|
124
|
-
const insertClasses = valueClasses.filter((valueClass) => !existingClasses.includes(valueClass));
|
|
125
|
-
mergedAttributes[key] = [...existingClasses, ...insertClasses].join(" ");
|
|
126
|
-
} else if (key === "style") {
|
|
127
|
-
const newStyles = value ? value.split(";").map((style) => style.trim()).filter(Boolean) : [];
|
|
128
|
-
const existingStyles = mergedAttributes[key] ? mergedAttributes[key].split(";").map((style) => style.trim()).filter(Boolean) : [];
|
|
129
|
-
const styleMap = /* @__PURE__ */ new Map();
|
|
130
|
-
existingStyles.forEach((style) => {
|
|
131
|
-
const [property, val] = style.split(":").map((part) => part.trim());
|
|
132
|
-
styleMap.set(property, val);
|
|
133
|
-
});
|
|
134
|
-
newStyles.forEach((style) => {
|
|
135
|
-
const [property, val] = style.split(":").map((part) => part.trim());
|
|
136
|
-
styleMap.set(property, val);
|
|
137
|
-
});
|
|
138
|
-
mergedAttributes[key] = Array.from(styleMap.entries()).map(([property, val]) => `${property}: ${val}`).join("; ");
|
|
139
|
-
} else {
|
|
140
|
-
mergedAttributes[key] = value;
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
return mergedAttributes;
|
|
144
|
-
}, {});
|
|
145
|
-
}
|
|
146
|
-
function isFunction(value) {
|
|
147
|
-
return typeof value === "function";
|
|
148
|
-
}
|
|
149
|
-
function callOrReturn(value, context = void 0, ...props) {
|
|
150
|
-
if (isFunction(value)) {
|
|
151
|
-
if (context) {
|
|
152
|
-
return value.bind(context)(...props);
|
|
153
|
-
}
|
|
154
|
-
return value(...props);
|
|
155
|
-
}
|
|
156
|
-
return value;
|
|
157
|
-
}
|
|
158
|
-
function isRegExp(value) {
|
|
159
|
-
return Object.prototype.toString.call(value) === "[object RegExp]";
|
|
160
|
-
}
|
|
161
|
-
function getType(value) {
|
|
162
|
-
return Object.prototype.toString.call(value).slice(8, -1);
|
|
163
|
-
}
|
|
164
|
-
function isPlainObject(value) {
|
|
165
|
-
if (getType(value) !== "Object") {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
|
|
169
|
-
}
|
|
170
|
-
function mergeDeep(target, source) {
|
|
171
|
-
const output = { ...target };
|
|
172
|
-
if (isPlainObject(target) && isPlainObject(source)) {
|
|
173
|
-
Object.keys(source).forEach((key) => {
|
|
174
|
-
if (isPlainObject(source[key]) && isPlainObject(target[key])) {
|
|
175
|
-
output[key] = mergeDeep(target[key], source[key]);
|
|
176
|
-
} else {
|
|
177
|
-
output[key] = source[key];
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
return output;
|
|
182
|
-
}
|
|
183
|
-
function getTextBetween(startNode, range, options) {
|
|
184
|
-
const { from, to } = range;
|
|
185
|
-
const { blockSeparator = "\n\n", textSerializers = {} } = options || {};
|
|
186
|
-
let text = "";
|
|
187
|
-
startNode.nodesBetween(from, to, (node, pos, parent, index) => {
|
|
188
|
-
var _a;
|
|
189
|
-
if (node.isBlock && pos > from) {
|
|
190
|
-
text += blockSeparator;
|
|
191
|
-
}
|
|
192
|
-
const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
|
|
193
|
-
if (textSerializer) {
|
|
194
|
-
if (parent) {
|
|
195
|
-
text += textSerializer({
|
|
196
|
-
node,
|
|
197
|
-
pos,
|
|
198
|
-
parent,
|
|
199
|
-
index,
|
|
200
|
-
range
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
if (node.isText) {
|
|
206
|
-
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
return text;
|
|
210
|
-
}
|
|
211
|
-
function getTextSerializersFromSchema(schema) {
|
|
212
|
-
return Object.fromEntries(Object.entries(schema.nodes).filter(([, node]) => node.spec.toText).map(([name, node]) => [name, node.spec.toText]));
|
|
213
|
-
}
|
|
214
|
-
function objectIncludes(object1, object2, options = { strict: true }) {
|
|
215
|
-
const keys = Object.keys(object2);
|
|
216
|
-
if (!keys.length) {
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
return keys.every((key) => {
|
|
220
|
-
if (options.strict) {
|
|
221
|
-
return object2[key] === object1[key];
|
|
222
|
-
}
|
|
223
|
-
if (isRegExp(object2[key])) {
|
|
224
|
-
return object2[key].test(object1[key]);
|
|
225
|
-
}
|
|
226
|
-
return object2[key] === object1[key];
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
function findMarkInSet(marks, type, attributes = {}) {
|
|
230
|
-
return marks.find((item) => {
|
|
231
|
-
return item.type === type && objectIncludes(
|
|
232
|
-
// Only check equality for the attributes that are provided
|
|
233
|
-
Object.fromEntries(Object.keys(attributes).map((k) => [k, item.attrs[k]])),
|
|
234
|
-
attributes
|
|
235
|
-
);
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
function isMarkInSet(marks, type, attributes = {}) {
|
|
239
|
-
return !!findMarkInSet(marks, type, attributes);
|
|
240
|
-
}
|
|
241
|
-
function getMarkRange($pos, type, attributes) {
|
|
242
|
-
var _a;
|
|
243
|
-
if (!$pos || !type) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
let start = $pos.parent.childAfter($pos.parentOffset);
|
|
247
|
-
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
|
|
248
|
-
start = $pos.parent.childBefore($pos.parentOffset);
|
|
249
|
-
}
|
|
250
|
-
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
attributes = attributes || ((_a = start.node.marks[0]) === null || _a === void 0 ? void 0 : _a.attrs);
|
|
254
|
-
const mark = findMarkInSet([...start.node.marks], type, attributes);
|
|
255
|
-
if (!mark) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
let startIndex = start.index;
|
|
259
|
-
let startPos = $pos.start() + start.offset;
|
|
260
|
-
let endIndex = startIndex + 1;
|
|
261
|
-
let endPos = startPos + start.node.nodeSize;
|
|
262
|
-
while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
|
|
263
|
-
startIndex -= 1;
|
|
264
|
-
startPos -= $pos.parent.child(startIndex).nodeSize;
|
|
265
|
-
}
|
|
266
|
-
while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
|
|
267
|
-
endPos += $pos.parent.child(endIndex).nodeSize;
|
|
268
|
-
endIndex += 1;
|
|
269
|
-
}
|
|
270
|
-
return {
|
|
271
|
-
from: startPos,
|
|
272
|
-
to: endPos
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
function getMarkType(nameOrType, schema) {
|
|
276
|
-
if (typeof nameOrType === "string") {
|
|
277
|
-
if (!schema.marks[nameOrType]) {
|
|
278
|
-
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
279
|
-
}
|
|
280
|
-
return schema.marks[nameOrType];
|
|
281
|
-
}
|
|
282
|
-
return nameOrType;
|
|
283
|
-
}
|
|
284
|
-
function isTextSelection(value) {
|
|
285
|
-
return value instanceof TextSelection;
|
|
286
|
-
}
|
|
287
|
-
function minMax(value = 0, min = 0, max = 0) {
|
|
288
|
-
return Math.min(Math.max(value, min), max);
|
|
289
|
-
}
|
|
290
|
-
function resolveFocusPosition(doc, position = null) {
|
|
291
|
-
if (!position) {
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
const selectionAtStart = Selection.atStart(doc);
|
|
295
|
-
const selectionAtEnd = Selection.atEnd(doc);
|
|
296
|
-
if (position === "start" || position === true) {
|
|
297
|
-
return selectionAtStart;
|
|
298
|
-
}
|
|
299
|
-
if (position === "end") {
|
|
300
|
-
return selectionAtEnd;
|
|
301
|
-
}
|
|
302
|
-
const minPos = selectionAtStart.from;
|
|
303
|
-
const maxPos = selectionAtEnd.to;
|
|
304
|
-
if (position === "all") {
|
|
305
|
-
return TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
|
|
306
|
-
}
|
|
307
|
-
return TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
|
|
308
|
-
}
|
|
309
|
-
function isAndroid() {
|
|
310
|
-
return navigator.platform === "Android" || /android/i.test(navigator.userAgent);
|
|
311
|
-
}
|
|
312
|
-
function isiOS() {
|
|
313
|
-
return [
|
|
314
|
-
"iPad Simulator",
|
|
315
|
-
"iPhone Simulator",
|
|
316
|
-
"iPod Simulator",
|
|
317
|
-
"iPad",
|
|
318
|
-
"iPhone",
|
|
319
|
-
"iPod"
|
|
320
|
-
].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
|
321
|
-
}
|
|
322
|
-
function isSafari() {
|
|
323
|
-
return typeof navigator !== "undefined" ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false;
|
|
324
|
-
}
|
|
325
|
-
function elementFromString(value) {
|
|
326
|
-
const wrappedValue = `<body>${value}</body>`;
|
|
327
|
-
const html = new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
|
|
328
|
-
return removeWhitespaces(html);
|
|
329
|
-
}
|
|
330
|
-
function createNodeFromContent(content, schema, options) {
|
|
331
|
-
if (content instanceof Node$1 || content instanceof Fragment) {
|
|
332
|
-
return content;
|
|
333
|
-
}
|
|
334
|
-
options = {
|
|
335
|
-
slice: true,
|
|
336
|
-
parseOptions: {},
|
|
337
|
-
...options
|
|
338
|
-
};
|
|
339
|
-
const isJSONContent = typeof content === "object" && content !== null;
|
|
340
|
-
const isTextContent = typeof content === "string";
|
|
341
|
-
if (isJSONContent) {
|
|
342
|
-
try {
|
|
343
|
-
const isArrayContent = Array.isArray(content) && content.length > 0;
|
|
344
|
-
if (isArrayContent) {
|
|
345
|
-
return Fragment.fromArray(content.map((item) => schema.nodeFromJSON(item)));
|
|
346
|
-
}
|
|
347
|
-
const node = schema.nodeFromJSON(content);
|
|
348
|
-
if (options.errorOnInvalidContent) {
|
|
349
|
-
node.check();
|
|
350
|
-
}
|
|
351
|
-
return node;
|
|
352
|
-
} catch (error) {
|
|
353
|
-
if (options.errorOnInvalidContent) {
|
|
354
|
-
throw new Error("[tiptap error]: Invalid JSON content", { cause: error });
|
|
355
|
-
}
|
|
356
|
-
console.warn("[tiptap warn]: Invalid content.", "Passed value:", content, "Error:", error);
|
|
357
|
-
return createNodeFromContent("", schema, options);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
if (isTextContent) {
|
|
361
|
-
if (options.errorOnInvalidContent) {
|
|
362
|
-
let hasInvalidContent = false;
|
|
363
|
-
let invalidContent = "";
|
|
364
|
-
const contentCheckSchema = new Schema({
|
|
365
|
-
topNode: schema.spec.topNode,
|
|
366
|
-
marks: schema.spec.marks,
|
|
367
|
-
// Prosemirror's schemas are executed such that: the last to execute, matches last
|
|
368
|
-
// This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
|
|
369
|
-
nodes: schema.spec.nodes.append({
|
|
370
|
-
__tiptap__private__unknown__catch__all__node: {
|
|
371
|
-
content: "inline*",
|
|
372
|
-
group: "block",
|
|
373
|
-
parseDOM: [
|
|
374
|
-
{
|
|
375
|
-
tag: "*",
|
|
376
|
-
getAttrs: (e) => {
|
|
377
|
-
hasInvalidContent = true;
|
|
378
|
-
invalidContent = typeof e === "string" ? e : e.outerHTML;
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
]
|
|
383
|
-
}
|
|
384
|
-
})
|
|
385
|
-
});
|
|
386
|
-
if (options.slice) {
|
|
387
|
-
DOMParser2.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
|
|
388
|
-
} else {
|
|
389
|
-
DOMParser2.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
|
|
390
|
-
}
|
|
391
|
-
if (options.errorOnInvalidContent && hasInvalidContent) {
|
|
392
|
-
throw new Error("[tiptap error]: Invalid HTML content", { cause: new Error(`Invalid element found: ${invalidContent}`) });
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
const parser = DOMParser2.fromSchema(schema);
|
|
396
|
-
if (options.slice) {
|
|
397
|
-
return parser.parseSlice(elementFromString(content), options.parseOptions).content;
|
|
398
|
-
}
|
|
399
|
-
return parser.parse(elementFromString(content), options.parseOptions);
|
|
400
|
-
}
|
|
401
|
-
return createNodeFromContent("", schema, options);
|
|
402
|
-
}
|
|
403
|
-
function selectionToInsertionEnd(tr, startLen, bias) {
|
|
404
|
-
const last = tr.steps.length - 1;
|
|
405
|
-
if (last < startLen) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const step = tr.steps[last];
|
|
409
|
-
if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
const map = tr.mapping.maps[last];
|
|
413
|
-
let end = 0;
|
|
414
|
-
map.forEach((_from, _to, _newFrom, newTo) => {
|
|
415
|
-
if (end === 0) {
|
|
416
|
-
end = newTo;
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
|
|
420
|
-
}
|
|
421
|
-
function isMacOS() {
|
|
422
|
-
return typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
|
|
423
|
-
}
|
|
424
|
-
function normalizeKeyName(name) {
|
|
425
|
-
const parts = name.split(/-(?!$)/);
|
|
426
|
-
let result = parts[parts.length - 1];
|
|
427
|
-
if (result === "Space") {
|
|
428
|
-
result = " ";
|
|
429
|
-
}
|
|
430
|
-
let alt;
|
|
431
|
-
let ctrl;
|
|
432
|
-
let shift;
|
|
433
|
-
let meta;
|
|
434
|
-
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
435
|
-
const mod = parts[i];
|
|
436
|
-
if (/^(cmd|meta|m)$/i.test(mod)) {
|
|
437
|
-
meta = true;
|
|
438
|
-
} else if (/^a(lt)?$/i.test(mod)) {
|
|
439
|
-
alt = true;
|
|
440
|
-
} else if (/^(c|ctrl|control)$/i.test(mod)) {
|
|
441
|
-
ctrl = true;
|
|
442
|
-
} else if (/^s(hift)?$/i.test(mod)) {
|
|
443
|
-
shift = true;
|
|
444
|
-
} else if (/^mod$/i.test(mod)) {
|
|
445
|
-
if (isiOS() || isMacOS()) {
|
|
446
|
-
meta = true;
|
|
447
|
-
} else {
|
|
448
|
-
ctrl = true;
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
throw new Error(`Unrecognized modifier name: ${mod}`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (alt) {
|
|
455
|
-
result = `Alt-${result}`;
|
|
456
|
-
}
|
|
457
|
-
if (ctrl) {
|
|
458
|
-
result = `Ctrl-${result}`;
|
|
459
|
-
}
|
|
460
|
-
if (meta) {
|
|
461
|
-
result = `Meta-${result}`;
|
|
462
|
-
}
|
|
463
|
-
if (shift) {
|
|
464
|
-
result = `Shift-${result}`;
|
|
465
|
-
}
|
|
466
|
-
return result;
|
|
467
|
-
}
|
|
468
|
-
function isNodeActive(state, typeOrName, attributes = {}) {
|
|
469
|
-
const { from, to, empty } = state.selection;
|
|
470
|
-
const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
|
|
471
|
-
const nodeRanges = [];
|
|
472
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
473
|
-
if (node.isText) {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
const relativeFrom = Math.max(from, pos);
|
|
477
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
478
|
-
nodeRanges.push({
|
|
479
|
-
node,
|
|
480
|
-
from: relativeFrom,
|
|
481
|
-
to: relativeTo
|
|
482
|
-
});
|
|
483
|
-
});
|
|
484
|
-
const selectionRange = to - from;
|
|
485
|
-
const matchedNodeRanges = nodeRanges.filter((nodeRange) => {
|
|
486
|
-
if (!type) {
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
return type.name === nodeRange.node.type.name;
|
|
490
|
-
}).filter((nodeRange) => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
|
|
491
|
-
if (empty) {
|
|
492
|
-
return !!matchedNodeRanges.length;
|
|
493
|
-
}
|
|
494
|
-
const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
|
|
495
|
-
return range >= selectionRange;
|
|
496
|
-
}
|
|
497
|
-
function getSchemaTypeNameByName(name, schema) {
|
|
498
|
-
if (schema.nodes[name]) {
|
|
499
|
-
return "node";
|
|
500
|
-
}
|
|
501
|
-
if (schema.marks[name]) {
|
|
502
|
-
return "mark";
|
|
503
|
-
}
|
|
504
|
-
return null;
|
|
505
|
-
}
|
|
506
|
-
function deleteProps(obj, propOrProps) {
|
|
507
|
-
const props = typeof propOrProps === "string" ? [propOrProps] : propOrProps;
|
|
508
|
-
return Object.keys(obj).reduce((newObj, prop) => {
|
|
509
|
-
if (!props.includes(prop)) {
|
|
510
|
-
newObj[prop] = obj[prop];
|
|
511
|
-
}
|
|
512
|
-
return newObj;
|
|
513
|
-
}, {});
|
|
514
|
-
}
|
|
515
|
-
function createDocument(content, schema, parseOptions = {}, options = {}) {
|
|
516
|
-
return createNodeFromContent(content, schema, {
|
|
517
|
-
slice: false,
|
|
518
|
-
parseOptions,
|
|
519
|
-
errorOnInvalidContent: options.errorOnInvalidContent
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
function getMarkAttributes(state, typeOrName) {
|
|
523
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
524
|
-
const { from, to, empty } = state.selection;
|
|
525
|
-
const marks = [];
|
|
526
|
-
if (empty) {
|
|
527
|
-
if (state.storedMarks) {
|
|
528
|
-
marks.push(...state.storedMarks);
|
|
529
|
-
}
|
|
530
|
-
marks.push(...state.selection.$head.marks());
|
|
531
|
-
} else {
|
|
532
|
-
state.doc.nodesBetween(from, to, (node) => {
|
|
533
|
-
marks.push(...node.marks);
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
const mark = marks.find((markItem) => markItem.type.name === type.name);
|
|
537
|
-
if (!mark) {
|
|
538
|
-
return {};
|
|
539
|
-
}
|
|
540
|
-
return { ...mark.attrs };
|
|
541
|
-
}
|
|
542
|
-
function defaultBlockAt(match) {
|
|
543
|
-
for (let i = 0; i < match.edgeCount; i += 1) {
|
|
544
|
-
const { type } = match.edge(i);
|
|
545
|
-
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
|
546
|
-
return type;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
function findParentNodeClosestToPos($pos, predicate) {
|
|
552
|
-
for (let i = $pos.depth; i > 0; i -= 1) {
|
|
553
|
-
const node = $pos.node(i);
|
|
554
|
-
if (predicate(node)) {
|
|
555
|
-
return {
|
|
556
|
-
pos: i > 0 ? $pos.before(i) : 0,
|
|
557
|
-
start: $pos.start(i),
|
|
558
|
-
depth: i,
|
|
559
|
-
node
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
function findParentNode(predicate) {
|
|
565
|
-
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
|
|
566
|
-
}
|
|
567
|
-
function getSplittedAttributes(extensionAttributes, typeName, attributes) {
|
|
568
|
-
return Object.fromEntries(Object.entries(attributes).filter(([name]) => {
|
|
569
|
-
const extensionAttribute = extensionAttributes.find((item) => {
|
|
570
|
-
return item.type === typeName && item.name === name;
|
|
571
|
-
});
|
|
572
|
-
if (!extensionAttribute) {
|
|
573
|
-
return false;
|
|
574
|
-
}
|
|
575
|
-
return extensionAttribute.attribute.keepOnSplit;
|
|
576
|
-
}));
|
|
577
|
-
}
|
|
578
|
-
function isMarkActive(state, typeOrName, attributes = {}) {
|
|
579
|
-
const { empty, ranges } = state.selection;
|
|
580
|
-
const type = typeOrName ? getMarkType(typeOrName, state.schema) : null;
|
|
581
|
-
if (empty) {
|
|
582
|
-
return !!(state.storedMarks || state.selection.$from.marks()).filter((mark) => {
|
|
583
|
-
if (!type) {
|
|
584
|
-
return true;
|
|
585
|
-
}
|
|
586
|
-
return type.name === mark.type.name;
|
|
587
|
-
}).find((mark) => objectIncludes(mark.attrs, attributes, { strict: false }));
|
|
588
|
-
}
|
|
589
|
-
let selectionRange = 0;
|
|
590
|
-
const markRanges = [];
|
|
591
|
-
ranges.forEach(({ $from, $to }) => {
|
|
592
|
-
const from = $from.pos;
|
|
593
|
-
const to = $to.pos;
|
|
594
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
595
|
-
if (!node.isText && !node.marks.length) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
const relativeFrom = Math.max(from, pos);
|
|
599
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
600
|
-
const range2 = relativeTo - relativeFrom;
|
|
601
|
-
selectionRange += range2;
|
|
602
|
-
markRanges.push(...node.marks.map((mark) => ({
|
|
603
|
-
mark,
|
|
604
|
-
from: relativeFrom,
|
|
605
|
-
to: relativeTo
|
|
606
|
-
})));
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
if (selectionRange === 0) {
|
|
610
|
-
return false;
|
|
611
|
-
}
|
|
612
|
-
const matchedRange = markRanges.filter((markRange) => {
|
|
613
|
-
if (!type) {
|
|
614
|
-
return true;
|
|
615
|
-
}
|
|
616
|
-
return type.name === markRange.mark.type.name;
|
|
617
|
-
}).filter((markRange) => objectIncludes(markRange.mark.attrs, attributes, { strict: false })).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
618
|
-
const excludedRange = markRanges.filter((markRange) => {
|
|
619
|
-
if (!type) {
|
|
620
|
-
return true;
|
|
621
|
-
}
|
|
622
|
-
return markRange.mark.type !== type && markRange.mark.type.excludes(type);
|
|
623
|
-
}).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
624
|
-
const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange;
|
|
625
|
-
return range >= selectionRange;
|
|
626
|
-
}
|
|
627
|
-
function isList(name, extensions) {
|
|
628
|
-
const { nodeExtensions } = splitExtensions(extensions);
|
|
629
|
-
const extension = nodeExtensions.find((item) => item.name === name);
|
|
630
|
-
if (!extension) {
|
|
631
|
-
return false;
|
|
632
|
-
}
|
|
633
|
-
const context = {
|
|
634
|
-
name: extension.name,
|
|
635
|
-
options: extension.options,
|
|
636
|
-
storage: extension.storage
|
|
637
|
-
};
|
|
638
|
-
const group = callOrReturn(getExtensionField(extension, "group", context));
|
|
639
|
-
if (typeof group !== "string") {
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
return group.split(" ").includes("list");
|
|
643
|
-
}
|
|
644
|
-
function isNodeEmpty(node, { checkChildren = true, ignoreWhitespace = false } = {}) {
|
|
645
|
-
var _a;
|
|
646
|
-
if (ignoreWhitespace) {
|
|
647
|
-
if (node.type.name === "hardBreak") {
|
|
648
|
-
return true;
|
|
649
|
-
}
|
|
650
|
-
if (node.isText) {
|
|
651
|
-
return /^\s*$/m.test((_a = node.text) !== null && _a !== void 0 ? _a : "");
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
if (node.isText) {
|
|
655
|
-
return !node.text;
|
|
656
|
-
}
|
|
657
|
-
if (node.isAtom || node.isLeaf) {
|
|
658
|
-
return false;
|
|
659
|
-
}
|
|
660
|
-
if (node.content.childCount === 0) {
|
|
661
|
-
return true;
|
|
662
|
-
}
|
|
663
|
-
if (checkChildren) {
|
|
664
|
-
let isContentEmpty = true;
|
|
665
|
-
node.content.forEach((childNode) => {
|
|
666
|
-
if (isContentEmpty === false) {
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {
|
|
670
|
-
isContentEmpty = false;
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
return isContentEmpty;
|
|
674
|
-
}
|
|
675
|
-
return false;
|
|
676
|
-
}
|
|
677
|
-
function canSetMark(state, tr, newMarkType) {
|
|
678
|
-
var _a;
|
|
679
|
-
const { selection } = tr;
|
|
680
|
-
let cursor = null;
|
|
681
|
-
if (isTextSelection(selection)) {
|
|
682
|
-
cursor = selection.$cursor;
|
|
683
|
-
}
|
|
684
|
-
if (cursor) {
|
|
685
|
-
const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
|
|
686
|
-
return !!newMarkType.isInSet(currentMarks) || !currentMarks.some((mark) => mark.type.excludes(newMarkType));
|
|
687
|
-
}
|
|
688
|
-
const { ranges } = selection;
|
|
689
|
-
return ranges.some(({ $from, $to }) => {
|
|
690
|
-
let someNodeSupportsMark = $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false;
|
|
691
|
-
state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
|
|
692
|
-
if (someNodeSupportsMark) {
|
|
693
|
-
return false;
|
|
694
|
-
}
|
|
695
|
-
if (node.isInline) {
|
|
696
|
-
const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
|
|
697
|
-
const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks) || !node.marks.some((otherMark) => otherMark.type.excludes(newMarkType));
|
|
698
|
-
someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
|
|
699
|
-
}
|
|
700
|
-
return !someNodeSupportsMark;
|
|
701
|
-
});
|
|
702
|
-
return someNodeSupportsMark;
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
function ensureMarks(state, splittableMarks) {
|
|
706
|
-
const marks = state.storedMarks || state.selection.$to.parentOffset && state.selection.$from.marks();
|
|
707
|
-
if (marks) {
|
|
708
|
-
const filteredMarks = marks.filter((mark) => splittableMarks === null || splittableMarks === void 0 ? void 0 : splittableMarks.includes(mark.type.name));
|
|
709
|
-
state.tr.ensureMarks(filteredMarks);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
var CommandManager, Extension, ClipboardTextSerializer, blur, clearContent, clearNodes, command, createParagraphNear, cut, deleteCurrentNode, deleteNode, deleteRange, deleteSelection, enter, exitCode, extendMarkRange, first, focus, forEach, insertContent, removeWhitespaces, isFragment, insertContentAt, joinUp, joinDown, joinBackward, joinForward, joinItemBackward, joinItemForward, joinTextblockBackward, joinTextblockForward, keyboardShortcut, lift, liftEmptyBlock, liftListItem, newlineInCode, resetAttributes, scrollIntoView, selectAll, selectNodeBackward, selectNodeForward, selectParentNode, selectTextblockEnd, selectTextblockStart, setContent, setMark, setMeta, setNode, setNodeSelection, setTextSelection, sinkListItem, splitBlock, splitListItem, joinListBackwards, joinListForwards, toggleList, toggleMark, toggleNode, toggleWrap, undoInputRule, unsetAllMarks, unsetMark, updateAttributes, wrapIn, wrapInList, commands, Commands, Drop, Editable, focusEventsPluginKey, FocusEvents, Keymap, Paste, Tabindex, Node;
|
|
713
|
-
var init_dist = __esm({
|
|
714
|
-
"node_modules/@tiptap/core/dist/index.js"() {
|
|
715
|
-
"use strict";
|
|
716
|
-
CommandManager = class {
|
|
717
|
-
constructor(props) {
|
|
718
|
-
this.editor = props.editor;
|
|
719
|
-
this.rawCommands = this.editor.extensionManager.commands;
|
|
720
|
-
this.customState = props.state;
|
|
721
|
-
}
|
|
722
|
-
get hasCustomState() {
|
|
723
|
-
return !!this.customState;
|
|
724
|
-
}
|
|
725
|
-
get state() {
|
|
726
|
-
return this.customState || this.editor.state;
|
|
727
|
-
}
|
|
728
|
-
get commands() {
|
|
729
|
-
const { rawCommands, editor, state } = this;
|
|
730
|
-
const { view } = editor;
|
|
731
|
-
const { tr } = state;
|
|
732
|
-
const props = this.buildProps(tr);
|
|
733
|
-
return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
734
|
-
const method = (...args) => {
|
|
735
|
-
const callback = command2(...args)(props);
|
|
736
|
-
if (!tr.getMeta("preventDispatch") && !this.hasCustomState) {
|
|
737
|
-
view.dispatch(tr);
|
|
738
|
-
}
|
|
739
|
-
return callback;
|
|
740
|
-
};
|
|
741
|
-
return [name, method];
|
|
742
|
-
}));
|
|
743
|
-
}
|
|
744
|
-
get chain() {
|
|
745
|
-
return () => this.createChain();
|
|
746
|
-
}
|
|
747
|
-
get can() {
|
|
748
|
-
return () => this.createCan();
|
|
749
|
-
}
|
|
750
|
-
createChain(startTr, shouldDispatch = true) {
|
|
751
|
-
const { rawCommands, editor, state } = this;
|
|
752
|
-
const { view } = editor;
|
|
753
|
-
const callbacks = [];
|
|
754
|
-
const hasStartTransaction = !!startTr;
|
|
755
|
-
const tr = startTr || state.tr;
|
|
756
|
-
const run = () => {
|
|
757
|
-
if (!hasStartTransaction && shouldDispatch && !tr.getMeta("preventDispatch") && !this.hasCustomState) {
|
|
758
|
-
view.dispatch(tr);
|
|
759
|
-
}
|
|
760
|
-
return callbacks.every((callback) => callback === true);
|
|
761
|
-
};
|
|
762
|
-
const chain = {
|
|
763
|
-
...Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
764
|
-
const chainedCommand = (...args) => {
|
|
765
|
-
const props = this.buildProps(tr, shouldDispatch);
|
|
766
|
-
const callback = command2(...args)(props);
|
|
767
|
-
callbacks.push(callback);
|
|
768
|
-
return chain;
|
|
769
|
-
};
|
|
770
|
-
return [name, chainedCommand];
|
|
771
|
-
})),
|
|
772
|
-
run
|
|
773
|
-
};
|
|
774
|
-
return chain;
|
|
775
|
-
}
|
|
776
|
-
createCan(startTr) {
|
|
777
|
-
const { rawCommands, state } = this;
|
|
778
|
-
const dispatch = false;
|
|
779
|
-
const tr = startTr || state.tr;
|
|
780
|
-
const props = this.buildProps(tr, dispatch);
|
|
781
|
-
const formattedCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
782
|
-
return [name, (...args) => command2(...args)({ ...props, dispatch: void 0 })];
|
|
783
|
-
}));
|
|
784
|
-
return {
|
|
785
|
-
...formattedCommands,
|
|
786
|
-
chain: () => this.createChain(tr, dispatch)
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
buildProps(tr, shouldDispatch = true) {
|
|
790
|
-
const { rawCommands, editor, state } = this;
|
|
791
|
-
const { view } = editor;
|
|
792
|
-
const props = {
|
|
793
|
-
tr,
|
|
794
|
-
editor,
|
|
795
|
-
view,
|
|
796
|
-
state: createChainableState({
|
|
797
|
-
state,
|
|
798
|
-
transaction: tr
|
|
799
|
-
}),
|
|
800
|
-
dispatch: shouldDispatch ? () => void 0 : void 0,
|
|
801
|
-
chain: () => this.createChain(tr, shouldDispatch),
|
|
802
|
-
can: () => this.createCan(tr),
|
|
803
|
-
get commands() {
|
|
804
|
-
return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
805
|
-
return [name, (...args) => command2(...args)(props)];
|
|
806
|
-
}));
|
|
807
|
-
}
|
|
808
|
-
};
|
|
809
|
-
return props;
|
|
810
|
-
}
|
|
811
|
-
};
|
|
812
|
-
Extension = class _Extension {
|
|
813
|
-
constructor(config = {}) {
|
|
814
|
-
this.type = "extension";
|
|
815
|
-
this.name = "extension";
|
|
816
|
-
this.parent = null;
|
|
817
|
-
this.child = null;
|
|
818
|
-
this.config = {
|
|
819
|
-
name: this.name,
|
|
820
|
-
defaultOptions: {}
|
|
821
|
-
};
|
|
822
|
-
this.config = {
|
|
823
|
-
...this.config,
|
|
824
|
-
...config
|
|
825
|
-
};
|
|
826
|
-
this.name = this.config.name;
|
|
827
|
-
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
828
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
829
|
-
}
|
|
830
|
-
this.options = this.config.defaultOptions;
|
|
831
|
-
if (this.config.addOptions) {
|
|
832
|
-
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
|
833
|
-
name: this.name
|
|
834
|
-
}));
|
|
835
|
-
}
|
|
836
|
-
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
|
837
|
-
name: this.name,
|
|
838
|
-
options: this.options
|
|
839
|
-
})) || {};
|
|
840
|
-
}
|
|
841
|
-
static create(config = {}) {
|
|
842
|
-
return new _Extension(config);
|
|
843
|
-
}
|
|
844
|
-
configure(options = {}) {
|
|
845
|
-
const extension = this.extend({
|
|
846
|
-
...this.config,
|
|
847
|
-
addOptions: () => {
|
|
848
|
-
return mergeDeep(this.options, options);
|
|
849
|
-
}
|
|
850
|
-
});
|
|
851
|
-
extension.name = this.name;
|
|
852
|
-
extension.parent = this.parent;
|
|
853
|
-
return extension;
|
|
854
|
-
}
|
|
855
|
-
extend(extendedConfig = {}) {
|
|
856
|
-
const extension = new _Extension({ ...this.config, ...extendedConfig });
|
|
857
|
-
extension.parent = this;
|
|
858
|
-
this.child = extension;
|
|
859
|
-
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
860
|
-
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
|
861
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
862
|
-
}
|
|
863
|
-
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
|
864
|
-
name: extension.name
|
|
865
|
-
}));
|
|
866
|
-
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
|
867
|
-
name: extension.name,
|
|
868
|
-
options: extension.options
|
|
869
|
-
}));
|
|
870
|
-
return extension;
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
|
-
ClipboardTextSerializer = Extension.create({
|
|
874
|
-
name: "clipboardTextSerializer",
|
|
875
|
-
addOptions() {
|
|
876
|
-
return {
|
|
877
|
-
blockSeparator: void 0
|
|
878
|
-
};
|
|
879
|
-
},
|
|
880
|
-
addProseMirrorPlugins() {
|
|
881
|
-
return [
|
|
882
|
-
new Plugin({
|
|
883
|
-
key: new PluginKey("clipboardTextSerializer"),
|
|
884
|
-
props: {
|
|
885
|
-
clipboardTextSerializer: () => {
|
|
886
|
-
const { editor } = this;
|
|
887
|
-
const { state, schema } = editor;
|
|
888
|
-
const { doc, selection } = state;
|
|
889
|
-
const { ranges } = selection;
|
|
890
|
-
const from = Math.min(...ranges.map((range2) => range2.$from.pos));
|
|
891
|
-
const to = Math.max(...ranges.map((range2) => range2.$to.pos));
|
|
892
|
-
const textSerializers = getTextSerializersFromSchema(schema);
|
|
893
|
-
const range = { from, to };
|
|
894
|
-
return getTextBetween(doc, range, {
|
|
895
|
-
...this.options.blockSeparator !== void 0 ? { blockSeparator: this.options.blockSeparator } : {},
|
|
896
|
-
textSerializers
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
})
|
|
901
|
-
];
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
blur = () => ({ editor, view }) => {
|
|
905
|
-
requestAnimationFrame(() => {
|
|
906
|
-
var _a;
|
|
907
|
-
if (!editor.isDestroyed) {
|
|
908
|
-
view.dom.blur();
|
|
909
|
-
(_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
return true;
|
|
913
|
-
};
|
|
914
|
-
clearContent = (emitUpdate = false) => ({ commands: commands2 }) => {
|
|
915
|
-
return commands2.setContent("", emitUpdate);
|
|
916
|
-
};
|
|
917
|
-
clearNodes = () => ({ state, tr, dispatch }) => {
|
|
918
|
-
const { selection } = tr;
|
|
919
|
-
const { ranges } = selection;
|
|
920
|
-
if (!dispatch) {
|
|
921
|
-
return true;
|
|
922
|
-
}
|
|
923
|
-
ranges.forEach(({ $from, $to }) => {
|
|
924
|
-
state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
|
|
925
|
-
if (node.type.isText) {
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
const { doc, mapping } = tr;
|
|
929
|
-
const $mappedFrom = doc.resolve(mapping.map(pos));
|
|
930
|
-
const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
|
|
931
|
-
const nodeRange = $mappedFrom.blockRange($mappedTo);
|
|
932
|
-
if (!nodeRange) {
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
const targetLiftDepth = liftTarget(nodeRange);
|
|
936
|
-
if (node.type.isTextblock) {
|
|
937
|
-
const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
|
|
938
|
-
tr.setNodeMarkup(nodeRange.start, defaultType);
|
|
939
|
-
}
|
|
940
|
-
if (targetLiftDepth || targetLiftDepth === 0) {
|
|
941
|
-
tr.lift(nodeRange, targetLiftDepth);
|
|
942
|
-
}
|
|
943
|
-
});
|
|
944
|
-
});
|
|
945
|
-
return true;
|
|
946
|
-
};
|
|
947
|
-
command = (fn) => (props) => {
|
|
948
|
-
return fn(props);
|
|
949
|
-
};
|
|
950
|
-
createParagraphNear = () => ({ state, dispatch }) => {
|
|
951
|
-
return createParagraphNear$1(state, dispatch);
|
|
952
|
-
};
|
|
953
|
-
cut = (originRange, targetPos) => ({ editor, tr }) => {
|
|
954
|
-
const { state } = editor;
|
|
955
|
-
const contentSlice = state.doc.slice(originRange.from, originRange.to);
|
|
956
|
-
tr.deleteRange(originRange.from, originRange.to);
|
|
957
|
-
const newPos = tr.mapping.map(targetPos);
|
|
958
|
-
tr.insert(newPos, contentSlice.content);
|
|
959
|
-
tr.setSelection(new TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))));
|
|
960
|
-
return true;
|
|
961
|
-
};
|
|
962
|
-
deleteCurrentNode = () => ({ tr, dispatch }) => {
|
|
963
|
-
const { selection } = tr;
|
|
964
|
-
const currentNode = selection.$anchor.node();
|
|
965
|
-
if (currentNode.content.size > 0) {
|
|
966
|
-
return false;
|
|
967
|
-
}
|
|
968
|
-
const $pos = tr.selection.$anchor;
|
|
969
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
970
|
-
const node = $pos.node(depth);
|
|
971
|
-
if (node.type === currentNode.type) {
|
|
972
|
-
if (dispatch) {
|
|
973
|
-
const from = $pos.before(depth);
|
|
974
|
-
const to = $pos.after(depth);
|
|
975
|
-
tr.delete(from, to).scrollIntoView();
|
|
976
|
-
}
|
|
977
|
-
return true;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
return false;
|
|
981
|
-
};
|
|
982
|
-
deleteNode = (typeOrName) => ({ tr, state, dispatch }) => {
|
|
983
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
984
|
-
const $pos = tr.selection.$anchor;
|
|
985
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
986
|
-
const node = $pos.node(depth);
|
|
987
|
-
if (node.type === type) {
|
|
988
|
-
if (dispatch) {
|
|
989
|
-
const from = $pos.before(depth);
|
|
990
|
-
const to = $pos.after(depth);
|
|
991
|
-
tr.delete(from, to).scrollIntoView();
|
|
992
|
-
}
|
|
993
|
-
return true;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
return false;
|
|
997
|
-
};
|
|
998
|
-
deleteRange = (range) => ({ tr, dispatch }) => {
|
|
999
|
-
const { from, to } = range;
|
|
1000
|
-
if (dispatch) {
|
|
1001
|
-
tr.delete(from, to);
|
|
1002
|
-
}
|
|
1003
|
-
return true;
|
|
1004
|
-
};
|
|
1005
|
-
deleteSelection = () => ({ state, dispatch }) => {
|
|
1006
|
-
return deleteSelection$1(state, dispatch);
|
|
1007
|
-
};
|
|
1008
|
-
enter = () => ({ commands: commands2 }) => {
|
|
1009
|
-
return commands2.keyboardShortcut("Enter");
|
|
1010
|
-
};
|
|
1011
|
-
exitCode = () => ({ state, dispatch }) => {
|
|
1012
|
-
return exitCode$1(state, dispatch);
|
|
1013
|
-
};
|
|
1014
|
-
extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1015
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1016
|
-
const { doc, selection } = tr;
|
|
1017
|
-
const { $from, from, to } = selection;
|
|
1018
|
-
if (dispatch) {
|
|
1019
|
-
const range = getMarkRange($from, type, attributes);
|
|
1020
|
-
if (range && range.from <= from && range.to >= to) {
|
|
1021
|
-
const newSelection = TextSelection.create(doc, range.from, range.to);
|
|
1022
|
-
tr.setSelection(newSelection);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
return true;
|
|
1026
|
-
};
|
|
1027
|
-
first = (commands2) => (props) => {
|
|
1028
|
-
const items = typeof commands2 === "function" ? commands2(props) : commands2;
|
|
1029
|
-
for (let i = 0; i < items.length; i += 1) {
|
|
1030
|
-
if (items[i](props)) {
|
|
1031
|
-
return true;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
return false;
|
|
1035
|
-
};
|
|
1036
|
-
focus = (position = null, options = {}) => ({ editor, view, tr, dispatch }) => {
|
|
1037
|
-
options = {
|
|
1038
|
-
scrollIntoView: true,
|
|
1039
|
-
...options
|
|
1040
|
-
};
|
|
1041
|
-
const delayedFocus = () => {
|
|
1042
|
-
if (isiOS() || isAndroid()) {
|
|
1043
|
-
view.dom.focus();
|
|
1044
|
-
}
|
|
1045
|
-
requestAnimationFrame(() => {
|
|
1046
|
-
if (!editor.isDestroyed) {
|
|
1047
|
-
view.focus();
|
|
1048
|
-
if (isSafari() && !isiOS() && !isAndroid()) {
|
|
1049
|
-
view.dom.focus({ preventScroll: true });
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
};
|
|
1054
|
-
if (view.hasFocus() && position === null || position === false) {
|
|
1055
|
-
return true;
|
|
1056
|
-
}
|
|
1057
|
-
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
|
1058
|
-
delayedFocus();
|
|
1059
|
-
return true;
|
|
1060
|
-
}
|
|
1061
|
-
const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
|
|
1062
|
-
const isSameSelection = editor.state.selection.eq(selection);
|
|
1063
|
-
if (dispatch) {
|
|
1064
|
-
if (!isSameSelection) {
|
|
1065
|
-
tr.setSelection(selection);
|
|
1066
|
-
}
|
|
1067
|
-
if (isSameSelection && tr.storedMarks) {
|
|
1068
|
-
tr.setStoredMarks(tr.storedMarks);
|
|
1069
|
-
}
|
|
1070
|
-
delayedFocus();
|
|
1071
|
-
}
|
|
1072
|
-
return true;
|
|
1073
|
-
};
|
|
1074
|
-
forEach = (items, fn) => (props) => {
|
|
1075
|
-
return items.every((item, index) => fn(item, { ...props, index }));
|
|
1076
|
-
};
|
|
1077
|
-
insertContent = (value, options) => ({ tr, commands: commands2 }) => {
|
|
1078
|
-
return commands2.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
|
|
1079
|
-
};
|
|
1080
|
-
removeWhitespaces = (node) => {
|
|
1081
|
-
const children = node.childNodes;
|
|
1082
|
-
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
1083
|
-
const child = children[i];
|
|
1084
|
-
if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
|
|
1085
|
-
node.removeChild(child);
|
|
1086
|
-
} else if (child.nodeType === 1) {
|
|
1087
|
-
removeWhitespaces(child);
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
return node;
|
|
1091
|
-
};
|
|
1092
|
-
isFragment = (nodeOrFragment) => {
|
|
1093
|
-
return !("type" in nodeOrFragment);
|
|
1094
|
-
};
|
|
1095
|
-
insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
|
|
1096
|
-
var _a;
|
|
1097
|
-
if (dispatch) {
|
|
1098
|
-
options = {
|
|
1099
|
-
parseOptions: editor.options.parseOptions,
|
|
1100
|
-
updateSelection: true,
|
|
1101
|
-
applyInputRules: false,
|
|
1102
|
-
applyPasteRules: false,
|
|
1103
|
-
...options
|
|
1104
|
-
};
|
|
1105
|
-
let content;
|
|
1106
|
-
const emitContentError = (error) => {
|
|
1107
|
-
editor.emit("contentError", {
|
|
1108
|
-
editor,
|
|
1109
|
-
error,
|
|
1110
|
-
disableCollaboration: () => {
|
|
1111
|
-
if (editor.storage.collaboration) {
|
|
1112
|
-
editor.storage.collaboration.isDisabled = true;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
});
|
|
1116
|
-
};
|
|
1117
|
-
const parseOptions = {
|
|
1118
|
-
preserveWhitespace: "full",
|
|
1119
|
-
...options.parseOptions
|
|
1120
|
-
};
|
|
1121
|
-
if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {
|
|
1122
|
-
try {
|
|
1123
|
-
createNodeFromContent(value, editor.schema, {
|
|
1124
|
-
parseOptions,
|
|
1125
|
-
errorOnInvalidContent: true
|
|
1126
|
-
});
|
|
1127
|
-
} catch (e) {
|
|
1128
|
-
emitContentError(e);
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
try {
|
|
1132
|
-
content = createNodeFromContent(value, editor.schema, {
|
|
1133
|
-
parseOptions,
|
|
1134
|
-
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
|
|
1135
|
-
});
|
|
1136
|
-
} catch (e) {
|
|
1137
|
-
emitContentError(e);
|
|
1138
|
-
return false;
|
|
1139
|
-
}
|
|
1140
|
-
let { from, to } = typeof position === "number" ? { from: position, to: position } : { from: position.from, to: position.to };
|
|
1141
|
-
let isOnlyTextContent = true;
|
|
1142
|
-
let isOnlyBlockContent = true;
|
|
1143
|
-
const nodes = isFragment(content) ? content : [content];
|
|
1144
|
-
nodes.forEach((node) => {
|
|
1145
|
-
node.check();
|
|
1146
|
-
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
|
|
1147
|
-
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
|
|
1148
|
-
});
|
|
1149
|
-
if (from === to && isOnlyBlockContent) {
|
|
1150
|
-
const { parent } = tr.doc.resolve(from);
|
|
1151
|
-
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
|
|
1152
|
-
if (isEmptyTextBlock) {
|
|
1153
|
-
from -= 1;
|
|
1154
|
-
to += 1;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
let newContent;
|
|
1158
|
-
if (isOnlyTextContent) {
|
|
1159
|
-
if (Array.isArray(value)) {
|
|
1160
|
-
newContent = value.map((v) => v.text || "").join("");
|
|
1161
|
-
} else if (value instanceof Fragment) {
|
|
1162
|
-
let text = "";
|
|
1163
|
-
value.forEach((node) => {
|
|
1164
|
-
if (node.text) {
|
|
1165
|
-
text += node.text;
|
|
1166
|
-
}
|
|
1167
|
-
});
|
|
1168
|
-
newContent = text;
|
|
1169
|
-
} else if (typeof value === "object" && !!value && !!value.text) {
|
|
1170
|
-
newContent = value.text;
|
|
1171
|
-
} else {
|
|
1172
|
-
newContent = value;
|
|
1173
|
-
}
|
|
1174
|
-
tr.insertText(newContent, from, to);
|
|
1175
|
-
} else {
|
|
1176
|
-
newContent = content;
|
|
1177
|
-
tr.replaceWith(from, to, newContent);
|
|
1178
|
-
}
|
|
1179
|
-
if (options.updateSelection) {
|
|
1180
|
-
selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
|
|
1181
|
-
}
|
|
1182
|
-
if (options.applyInputRules) {
|
|
1183
|
-
tr.setMeta("applyInputRules", { from, text: newContent });
|
|
1184
|
-
}
|
|
1185
|
-
if (options.applyPasteRules) {
|
|
1186
|
-
tr.setMeta("applyPasteRules", { from, text: newContent });
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
return true;
|
|
1190
|
-
};
|
|
1191
|
-
joinUp = () => ({ state, dispatch }) => {
|
|
1192
|
-
return joinUp$1(state, dispatch);
|
|
1193
|
-
};
|
|
1194
|
-
joinDown = () => ({ state, dispatch }) => {
|
|
1195
|
-
return joinDown$1(state, dispatch);
|
|
1196
|
-
};
|
|
1197
|
-
joinBackward = () => ({ state, dispatch }) => {
|
|
1198
|
-
return joinBackward$1(state, dispatch);
|
|
1199
|
-
};
|
|
1200
|
-
joinForward = () => ({ state, dispatch }) => {
|
|
1201
|
-
return joinForward$1(state, dispatch);
|
|
1202
|
-
};
|
|
1203
|
-
joinItemBackward = () => ({ state, dispatch, tr }) => {
|
|
1204
|
-
try {
|
|
1205
|
-
const point = joinPoint(state.doc, state.selection.$from.pos, -1);
|
|
1206
|
-
if (point === null || point === void 0) {
|
|
1207
|
-
return false;
|
|
1208
|
-
}
|
|
1209
|
-
tr.join(point, 2);
|
|
1210
|
-
if (dispatch) {
|
|
1211
|
-
dispatch(tr);
|
|
1212
|
-
}
|
|
1213
|
-
return true;
|
|
1214
|
-
} catch {
|
|
1215
|
-
return false;
|
|
1216
|
-
}
|
|
1217
|
-
};
|
|
1218
|
-
joinItemForward = () => ({ state, dispatch, tr }) => {
|
|
1219
|
-
try {
|
|
1220
|
-
const point = joinPoint(state.doc, state.selection.$from.pos, 1);
|
|
1221
|
-
if (point === null || point === void 0) {
|
|
1222
|
-
return false;
|
|
1223
|
-
}
|
|
1224
|
-
tr.join(point, 2);
|
|
1225
|
-
if (dispatch) {
|
|
1226
|
-
dispatch(tr);
|
|
1227
|
-
}
|
|
1228
|
-
return true;
|
|
1229
|
-
} catch {
|
|
1230
|
-
return false;
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
joinTextblockBackward = () => ({ state, dispatch }) => {
|
|
1234
|
-
return joinTextblockBackward$1(state, dispatch);
|
|
1235
|
-
};
|
|
1236
|
-
joinTextblockForward = () => ({ state, dispatch }) => {
|
|
1237
|
-
return joinTextblockForward$1(state, dispatch);
|
|
1238
|
-
};
|
|
1239
|
-
keyboardShortcut = (name) => ({ editor, view, tr, dispatch }) => {
|
|
1240
|
-
const keys = normalizeKeyName(name).split(/-(?!$)/);
|
|
1241
|
-
const key = keys.find((item) => !["Alt", "Ctrl", "Meta", "Shift"].includes(item));
|
|
1242
|
-
const event = new KeyboardEvent("keydown", {
|
|
1243
|
-
key: key === "Space" ? " " : key,
|
|
1244
|
-
altKey: keys.includes("Alt"),
|
|
1245
|
-
ctrlKey: keys.includes("Ctrl"),
|
|
1246
|
-
metaKey: keys.includes("Meta"),
|
|
1247
|
-
shiftKey: keys.includes("Shift"),
|
|
1248
|
-
bubbles: true,
|
|
1249
|
-
cancelable: true
|
|
1250
|
-
});
|
|
1251
|
-
const capturedTransaction = editor.captureTransaction(() => {
|
|
1252
|
-
view.someProp("handleKeyDown", (f) => f(view, event));
|
|
1253
|
-
});
|
|
1254
|
-
capturedTransaction === null || capturedTransaction === void 0 ? void 0 : capturedTransaction.steps.forEach((step) => {
|
|
1255
|
-
const newStep = step.map(tr.mapping);
|
|
1256
|
-
if (newStep && dispatch) {
|
|
1257
|
-
tr.maybeStep(newStep);
|
|
1258
|
-
}
|
|
1259
|
-
});
|
|
1260
|
-
return true;
|
|
1261
|
-
};
|
|
1262
|
-
lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1263
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1264
|
-
const isActive = isNodeActive(state, type, attributes);
|
|
1265
|
-
if (!isActive) {
|
|
1266
|
-
return false;
|
|
1267
|
-
}
|
|
1268
|
-
return lift$1(state, dispatch);
|
|
1269
|
-
};
|
|
1270
|
-
liftEmptyBlock = () => ({ state, dispatch }) => {
|
|
1271
|
-
return liftEmptyBlock$1(state, dispatch);
|
|
1272
|
-
};
|
|
1273
|
-
liftListItem = (typeOrName) => ({ state, dispatch }) => {
|
|
1274
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1275
|
-
return liftListItem$1(type)(state, dispatch);
|
|
1276
|
-
};
|
|
1277
|
-
newlineInCode = () => ({ state, dispatch }) => {
|
|
1278
|
-
return newlineInCode$1(state, dispatch);
|
|
1279
|
-
};
|
|
1280
|
-
resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
|
|
1281
|
-
let nodeType = null;
|
|
1282
|
-
let markType = null;
|
|
1283
|
-
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
|
1284
|
-
if (!schemaType) {
|
|
1285
|
-
return false;
|
|
1286
|
-
}
|
|
1287
|
-
if (schemaType === "node") {
|
|
1288
|
-
nodeType = getNodeType(typeOrName, state.schema);
|
|
1289
|
-
}
|
|
1290
|
-
if (schemaType === "mark") {
|
|
1291
|
-
markType = getMarkType(typeOrName, state.schema);
|
|
1292
|
-
}
|
|
1293
|
-
if (dispatch) {
|
|
1294
|
-
tr.selection.ranges.forEach((range) => {
|
|
1295
|
-
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
|
1296
|
-
if (nodeType && nodeType === node.type) {
|
|
1297
|
-
tr.setNodeMarkup(pos, void 0, deleteProps(node.attrs, attributes));
|
|
1298
|
-
}
|
|
1299
|
-
if (markType && node.marks.length) {
|
|
1300
|
-
node.marks.forEach((mark) => {
|
|
1301
|
-
if (markType === mark.type) {
|
|
1302
|
-
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
|
|
1303
|
-
}
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
});
|
|
1307
|
-
});
|
|
1308
|
-
}
|
|
1309
|
-
return true;
|
|
1310
|
-
};
|
|
1311
|
-
scrollIntoView = () => ({ tr, dispatch }) => {
|
|
1312
|
-
if (dispatch) {
|
|
1313
|
-
tr.scrollIntoView();
|
|
1314
|
-
}
|
|
1315
|
-
return true;
|
|
1316
|
-
};
|
|
1317
|
-
selectAll = () => ({ tr, dispatch }) => {
|
|
1318
|
-
if (dispatch) {
|
|
1319
|
-
const selection = new AllSelection(tr.doc);
|
|
1320
|
-
tr.setSelection(selection);
|
|
1321
|
-
}
|
|
1322
|
-
return true;
|
|
1323
|
-
};
|
|
1324
|
-
selectNodeBackward = () => ({ state, dispatch }) => {
|
|
1325
|
-
return selectNodeBackward$1(state, dispatch);
|
|
1326
|
-
};
|
|
1327
|
-
selectNodeForward = () => ({ state, dispatch }) => {
|
|
1328
|
-
return selectNodeForward$1(state, dispatch);
|
|
1329
|
-
};
|
|
1330
|
-
selectParentNode = () => ({ state, dispatch }) => {
|
|
1331
|
-
return selectParentNode$1(state, dispatch);
|
|
1332
|
-
};
|
|
1333
|
-
selectTextblockEnd = () => ({ state, dispatch }) => {
|
|
1334
|
-
return selectTextblockEnd$1(state, dispatch);
|
|
1335
|
-
};
|
|
1336
|
-
selectTextblockStart = () => ({ state, dispatch }) => {
|
|
1337
|
-
return selectTextblockStart$1(state, dispatch);
|
|
1338
|
-
};
|
|
1339
|
-
setContent = (content, emitUpdate = false, parseOptions = {}, options = {}) => ({ editor, tr, dispatch, commands: commands2 }) => {
|
|
1340
|
-
var _a, _b;
|
|
1341
|
-
const { doc } = tr;
|
|
1342
|
-
if (parseOptions.preserveWhitespace !== "full") {
|
|
1343
|
-
const document2 = createDocument(content, editor.schema, parseOptions, {
|
|
1344
|
-
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
|
|
1345
|
-
});
|
|
1346
|
-
if (dispatch) {
|
|
1347
|
-
tr.replaceWith(0, doc.content.size, document2).setMeta("preventUpdate", !emitUpdate);
|
|
1348
|
-
}
|
|
1349
|
-
return true;
|
|
1350
|
-
}
|
|
1351
|
-
if (dispatch) {
|
|
1352
|
-
tr.setMeta("preventUpdate", !emitUpdate);
|
|
1353
|
-
}
|
|
1354
|
-
return commands2.insertContentAt({ from: 0, to: doc.content.size }, content, {
|
|
1355
|
-
parseOptions,
|
|
1356
|
-
errorOnInvalidContent: (_b = options.errorOnInvalidContent) !== null && _b !== void 0 ? _b : editor.options.enableContentCheck
|
|
1357
|
-
});
|
|
1358
|
-
};
|
|
1359
|
-
setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1360
|
-
const { selection } = tr;
|
|
1361
|
-
const { empty, ranges } = selection;
|
|
1362
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1363
|
-
if (dispatch) {
|
|
1364
|
-
if (empty) {
|
|
1365
|
-
const oldAttributes = getMarkAttributes(state, type);
|
|
1366
|
-
tr.addStoredMark(type.create({
|
|
1367
|
-
...oldAttributes,
|
|
1368
|
-
...attributes
|
|
1369
|
-
}));
|
|
1370
|
-
} else {
|
|
1371
|
-
ranges.forEach((range) => {
|
|
1372
|
-
const from = range.$from.pos;
|
|
1373
|
-
const to = range.$to.pos;
|
|
1374
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1375
|
-
const trimmedFrom = Math.max(pos, from);
|
|
1376
|
-
const trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
1377
|
-
const someHasMark = node.marks.find((mark) => mark.type === type);
|
|
1378
|
-
if (someHasMark) {
|
|
1379
|
-
node.marks.forEach((mark) => {
|
|
1380
|
-
if (type === mark.type) {
|
|
1381
|
-
tr.addMark(trimmedFrom, trimmedTo, type.create({
|
|
1382
|
-
...mark.attrs,
|
|
1383
|
-
...attributes
|
|
1384
|
-
}));
|
|
1385
|
-
}
|
|
1386
|
-
});
|
|
1387
|
-
} else {
|
|
1388
|
-
tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
|
|
1389
|
-
}
|
|
1390
|
-
});
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
return canSetMark(state, tr, type);
|
|
1395
|
-
};
|
|
1396
|
-
setMeta = (key, value) => ({ tr }) => {
|
|
1397
|
-
tr.setMeta(key, value);
|
|
1398
|
-
return true;
|
|
1399
|
-
};
|
|
1400
|
-
setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
|
|
1401
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1402
|
-
let attributesToCopy;
|
|
1403
|
-
if (state.selection.$anchor.sameParent(state.selection.$head)) {
|
|
1404
|
-
attributesToCopy = state.selection.$anchor.parent.attrs;
|
|
1405
|
-
}
|
|
1406
|
-
if (!type.isTextblock) {
|
|
1407
|
-
console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
|
|
1408
|
-
return false;
|
|
1409
|
-
}
|
|
1410
|
-
return chain().command(({ commands: commands2 }) => {
|
|
1411
|
-
const canSetBlock = setBlockType(type, { ...attributesToCopy, ...attributes })(state);
|
|
1412
|
-
if (canSetBlock) {
|
|
1413
|
-
return true;
|
|
1414
|
-
}
|
|
1415
|
-
return commands2.clearNodes();
|
|
1416
|
-
}).command(({ state: updatedState }) => {
|
|
1417
|
-
return setBlockType(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch);
|
|
1418
|
-
}).run();
|
|
1419
|
-
};
|
|
1420
|
-
setNodeSelection = (position) => ({ tr, dispatch }) => {
|
|
1421
|
-
if (dispatch) {
|
|
1422
|
-
const { doc } = tr;
|
|
1423
|
-
const from = minMax(position, 0, doc.content.size);
|
|
1424
|
-
const selection = NodeSelection.create(doc, from);
|
|
1425
|
-
tr.setSelection(selection);
|
|
1426
|
-
}
|
|
1427
|
-
return true;
|
|
1428
|
-
};
|
|
1429
|
-
setTextSelection = (position) => ({ tr, dispatch }) => {
|
|
1430
|
-
if (dispatch) {
|
|
1431
|
-
const { doc } = tr;
|
|
1432
|
-
const { from, to } = typeof position === "number" ? { from: position, to: position } : position;
|
|
1433
|
-
const minPos = TextSelection.atStart(doc).from;
|
|
1434
|
-
const maxPos = TextSelection.atEnd(doc).to;
|
|
1435
|
-
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
1436
|
-
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
1437
|
-
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
|
|
1438
|
-
tr.setSelection(selection);
|
|
1439
|
-
}
|
|
1440
|
-
return true;
|
|
1441
|
-
};
|
|
1442
|
-
sinkListItem = (typeOrName) => ({ state, dispatch }) => {
|
|
1443
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1444
|
-
return sinkListItem$1(type)(state, dispatch);
|
|
1445
|
-
};
|
|
1446
|
-
splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor }) => {
|
|
1447
|
-
const { selection, doc } = tr;
|
|
1448
|
-
const { $from, $to } = selection;
|
|
1449
|
-
const extensionAttributes = editor.extensionManager.attributes;
|
|
1450
|
-
const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
|
|
1451
|
-
if (selection instanceof NodeSelection && selection.node.isBlock) {
|
|
1452
|
-
if (!$from.parentOffset || !canSplit(doc, $from.pos)) {
|
|
1453
|
-
return false;
|
|
1454
|
-
}
|
|
1455
|
-
if (dispatch) {
|
|
1456
|
-
if (keepMarks) {
|
|
1457
|
-
ensureMarks(state, editor.extensionManager.splittableMarks);
|
|
1458
|
-
}
|
|
1459
|
-
tr.split($from.pos).scrollIntoView();
|
|
1460
|
-
}
|
|
1461
|
-
return true;
|
|
1462
|
-
}
|
|
1463
|
-
if (!$from.parent.isBlock) {
|
|
1464
|
-
return false;
|
|
1465
|
-
}
|
|
1466
|
-
const atEnd = $to.parentOffset === $to.parent.content.size;
|
|
1467
|
-
const deflt = $from.depth === 0 ? void 0 : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
|
|
1468
|
-
let types = atEnd && deflt ? [
|
|
1469
|
-
{
|
|
1470
|
-
type: deflt,
|
|
1471
|
-
attrs: newAttributes
|
|
1472
|
-
}
|
|
1473
|
-
] : void 0;
|
|
1474
|
-
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
|
|
1475
|
-
if (!types && !can && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : void 0)) {
|
|
1476
|
-
can = true;
|
|
1477
|
-
types = deflt ? [
|
|
1478
|
-
{
|
|
1479
|
-
type: deflt,
|
|
1480
|
-
attrs: newAttributes
|
|
1481
|
-
}
|
|
1482
|
-
] : void 0;
|
|
1483
|
-
}
|
|
1484
|
-
if (dispatch) {
|
|
1485
|
-
if (can) {
|
|
1486
|
-
if (selection instanceof TextSelection) {
|
|
1487
|
-
tr.deleteSelection();
|
|
1488
|
-
}
|
|
1489
|
-
tr.split(tr.mapping.map($from.pos), 1, types);
|
|
1490
|
-
if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
|
|
1491
|
-
const first2 = tr.mapping.map($from.before());
|
|
1492
|
-
const $first = tr.doc.resolve(first2);
|
|
1493
|
-
if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
|
|
1494
|
-
tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
if (keepMarks) {
|
|
1499
|
-
ensureMarks(state, editor.extensionManager.splittableMarks);
|
|
1500
|
-
}
|
|
1501
|
-
tr.scrollIntoView();
|
|
1502
|
-
}
|
|
1503
|
-
return can;
|
|
1504
|
-
};
|
|
1505
|
-
splitListItem = (typeOrName, overrideAttrs = {}) => ({ tr, state, dispatch, editor }) => {
|
|
1506
|
-
var _a;
|
|
1507
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1508
|
-
const { $from, $to } = state.selection;
|
|
1509
|
-
const node = state.selection.node;
|
|
1510
|
-
if (node && node.isBlock || $from.depth < 2 || !$from.sameParent($to)) {
|
|
1511
|
-
return false;
|
|
1512
|
-
}
|
|
1513
|
-
const grandParent = $from.node(-1);
|
|
1514
|
-
if (grandParent.type !== type) {
|
|
1515
|
-
return false;
|
|
1516
|
-
}
|
|
1517
|
-
const extensionAttributes = editor.extensionManager.attributes;
|
|
1518
|
-
if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {
|
|
1519
|
-
if ($from.depth === 2 || $from.node(-3).type !== type || $from.index(-2) !== $from.node(-2).childCount - 1) {
|
|
1520
|
-
return false;
|
|
1521
|
-
}
|
|
1522
|
-
if (dispatch) {
|
|
1523
|
-
let wrap = Fragment.empty;
|
|
1524
|
-
const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
|
1525
|
-
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
|
|
1526
|
-
wrap = Fragment.from($from.node(d).copy(wrap));
|
|
1527
|
-
}
|
|
1528
|
-
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
|
1529
|
-
const newNextTypeAttributes2 = {
|
|
1530
|
-
...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
|
|
1531
|
-
...overrideAttrs
|
|
1532
|
-
};
|
|
1533
|
-
const nextType2 = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes2)) || void 0;
|
|
1534
|
-
wrap = wrap.append(Fragment.from(type.createAndFill(null, nextType2) || void 0));
|
|
1535
|
-
const start = $from.before($from.depth - (depthBefore - 1));
|
|
1536
|
-
tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
|
|
1537
|
-
let sel = -1;
|
|
1538
|
-
tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
|
|
1539
|
-
if (sel > -1) {
|
|
1540
|
-
return false;
|
|
1541
|
-
}
|
|
1542
|
-
if (n.isTextblock && n.content.size === 0) {
|
|
1543
|
-
sel = pos + 1;
|
|
1544
|
-
}
|
|
1545
|
-
});
|
|
1546
|
-
if (sel > -1) {
|
|
1547
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve(sel)));
|
|
1548
|
-
}
|
|
1549
|
-
tr.scrollIntoView();
|
|
1550
|
-
}
|
|
1551
|
-
return true;
|
|
1552
|
-
}
|
|
1553
|
-
const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
|
1554
|
-
const newTypeAttributes = {
|
|
1555
|
-
...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),
|
|
1556
|
-
...overrideAttrs
|
|
1557
|
-
};
|
|
1558
|
-
const newNextTypeAttributes = {
|
|
1559
|
-
...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
|
|
1560
|
-
...overrideAttrs
|
|
1561
|
-
};
|
|
1562
|
-
tr.delete($from.pos, $to.pos);
|
|
1563
|
-
const types = nextType ? [
|
|
1564
|
-
{ type, attrs: newTypeAttributes },
|
|
1565
|
-
{ type: nextType, attrs: newNextTypeAttributes }
|
|
1566
|
-
] : [{ type, attrs: newTypeAttributes }];
|
|
1567
|
-
if (!canSplit(tr.doc, $from.pos, 2)) {
|
|
1568
|
-
return false;
|
|
1569
|
-
}
|
|
1570
|
-
if (dispatch) {
|
|
1571
|
-
const { selection, storedMarks } = state;
|
|
1572
|
-
const { splittableMarks } = editor.extensionManager;
|
|
1573
|
-
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
|
1574
|
-
tr.split($from.pos, 2, types).scrollIntoView();
|
|
1575
|
-
if (!marks || !dispatch) {
|
|
1576
|
-
return true;
|
|
1577
|
-
}
|
|
1578
|
-
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
|
1579
|
-
tr.ensureMarks(filteredMarks);
|
|
1580
|
-
}
|
|
1581
|
-
return true;
|
|
1582
|
-
};
|
|
1583
|
-
joinListBackwards = (tr, listType) => {
|
|
1584
|
-
const list = findParentNode((node) => node.type === listType)(tr.selection);
|
|
1585
|
-
if (!list) {
|
|
1586
|
-
return true;
|
|
1587
|
-
}
|
|
1588
|
-
const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
|
|
1589
|
-
if (before === void 0) {
|
|
1590
|
-
return true;
|
|
1591
|
-
}
|
|
1592
|
-
const nodeBefore = tr.doc.nodeAt(before);
|
|
1593
|
-
const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) && canJoin(tr.doc, list.pos);
|
|
1594
|
-
if (!canJoinBackwards) {
|
|
1595
|
-
return true;
|
|
1596
|
-
}
|
|
1597
|
-
tr.join(list.pos);
|
|
1598
|
-
return true;
|
|
1599
|
-
};
|
|
1600
|
-
joinListForwards = (tr, listType) => {
|
|
1601
|
-
const list = findParentNode((node) => node.type === listType)(tr.selection);
|
|
1602
|
-
if (!list) {
|
|
1603
|
-
return true;
|
|
1604
|
-
}
|
|
1605
|
-
const after = tr.doc.resolve(list.start).after(list.depth);
|
|
1606
|
-
if (after === void 0) {
|
|
1607
|
-
return true;
|
|
1608
|
-
}
|
|
1609
|
-
const nodeAfter = tr.doc.nodeAt(after);
|
|
1610
|
-
const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type) && canJoin(tr.doc, after);
|
|
1611
|
-
if (!canJoinForwards) {
|
|
1612
|
-
return true;
|
|
1613
|
-
}
|
|
1614
|
-
tr.join(after);
|
|
1615
|
-
return true;
|
|
1616
|
-
};
|
|
1617
|
-
toggleList = (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) => ({ editor, tr, state, dispatch, chain, commands: commands2, can }) => {
|
|
1618
|
-
const { extensions, splittableMarks } = editor.extensionManager;
|
|
1619
|
-
const listType = getNodeType(listTypeOrName, state.schema);
|
|
1620
|
-
const itemType = getNodeType(itemTypeOrName, state.schema);
|
|
1621
|
-
const { selection, storedMarks } = state;
|
|
1622
|
-
const { $from, $to } = selection;
|
|
1623
|
-
const range = $from.blockRange($to);
|
|
1624
|
-
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
|
1625
|
-
if (!range) {
|
|
1626
|
-
return false;
|
|
1627
|
-
}
|
|
1628
|
-
const parentList = findParentNode((node) => isList(node.type.name, extensions))(selection);
|
|
1629
|
-
if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
|
|
1630
|
-
if (parentList.node.type === listType) {
|
|
1631
|
-
return commands2.liftListItem(itemType);
|
|
1632
|
-
}
|
|
1633
|
-
if (isList(parentList.node.type.name, extensions) && listType.validContent(parentList.node.content) && dispatch) {
|
|
1634
|
-
return chain().command(() => {
|
|
1635
|
-
tr.setNodeMarkup(parentList.pos, listType);
|
|
1636
|
-
return true;
|
|
1637
|
-
}).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
if (!keepMarks || !marks || !dispatch) {
|
|
1641
|
-
return chain().command(() => {
|
|
1642
|
-
const canWrapInList = can().wrapInList(listType, attributes);
|
|
1643
|
-
if (canWrapInList) {
|
|
1644
|
-
return true;
|
|
1645
|
-
}
|
|
1646
|
-
return commands2.clearNodes();
|
|
1647
|
-
}).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
|
1648
|
-
}
|
|
1649
|
-
return chain().command(() => {
|
|
1650
|
-
const canWrapInList = can().wrapInList(listType, attributes);
|
|
1651
|
-
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
|
1652
|
-
tr.ensureMarks(filteredMarks);
|
|
1653
|
-
if (canWrapInList) {
|
|
1654
|
-
return true;
|
|
1655
|
-
}
|
|
1656
|
-
return commands2.clearNodes();
|
|
1657
|
-
}).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
|
1658
|
-
};
|
|
1659
|
-
toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands: commands2 }) => {
|
|
1660
|
-
const { extendEmptyMarkRange = false } = options;
|
|
1661
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1662
|
-
const isActive = isMarkActive(state, type, attributes);
|
|
1663
|
-
if (isActive) {
|
|
1664
|
-
return commands2.unsetMark(type, { extendEmptyMarkRange });
|
|
1665
|
-
}
|
|
1666
|
-
return commands2.setMark(type, attributes);
|
|
1667
|
-
};
|
|
1668
|
-
toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
|
|
1669
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1670
|
-
const toggleType = getNodeType(toggleTypeOrName, state.schema);
|
|
1671
|
-
const isActive = isNodeActive(state, type, attributes);
|
|
1672
|
-
let attributesToCopy;
|
|
1673
|
-
if (state.selection.$anchor.sameParent(state.selection.$head)) {
|
|
1674
|
-
attributesToCopy = state.selection.$anchor.parent.attrs;
|
|
1675
|
-
}
|
|
1676
|
-
if (isActive) {
|
|
1677
|
-
return commands2.setNode(toggleType, attributesToCopy);
|
|
1678
|
-
}
|
|
1679
|
-
return commands2.setNode(type, { ...attributesToCopy, ...attributes });
|
|
1680
|
-
};
|
|
1681
|
-
toggleWrap = (typeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
|
|
1682
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1683
|
-
const isActive = isNodeActive(state, type, attributes);
|
|
1684
|
-
if (isActive) {
|
|
1685
|
-
return commands2.lift(type);
|
|
1686
|
-
}
|
|
1687
|
-
return commands2.wrapIn(type, attributes);
|
|
1688
|
-
};
|
|
1689
|
-
undoInputRule = () => ({ state, dispatch }) => {
|
|
1690
|
-
const plugins = state.plugins;
|
|
1691
|
-
for (let i = 0; i < plugins.length; i += 1) {
|
|
1692
|
-
const plugin = plugins[i];
|
|
1693
|
-
let undoable;
|
|
1694
|
-
if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
|
|
1695
|
-
if (dispatch) {
|
|
1696
|
-
const tr = state.tr;
|
|
1697
|
-
const toUndo = undoable.transform;
|
|
1698
|
-
for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
|
|
1699
|
-
tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
|
|
1700
|
-
}
|
|
1701
|
-
if (undoable.text) {
|
|
1702
|
-
const marks = tr.doc.resolve(undoable.from).marks();
|
|
1703
|
-
tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
|
|
1704
|
-
} else {
|
|
1705
|
-
tr.delete(undoable.from, undoable.to);
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
return true;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
return false;
|
|
1712
|
-
};
|
|
1713
|
-
unsetAllMarks = () => ({ tr, dispatch }) => {
|
|
1714
|
-
const { selection } = tr;
|
|
1715
|
-
const { empty, ranges } = selection;
|
|
1716
|
-
if (empty) {
|
|
1717
|
-
return true;
|
|
1718
|
-
}
|
|
1719
|
-
if (dispatch) {
|
|
1720
|
-
ranges.forEach((range) => {
|
|
1721
|
-
tr.removeMark(range.$from.pos, range.$to.pos);
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1724
|
-
return true;
|
|
1725
|
-
};
|
|
1726
|
-
unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
|
|
1727
|
-
var _a;
|
|
1728
|
-
const { extendEmptyMarkRange = false } = options;
|
|
1729
|
-
const { selection } = tr;
|
|
1730
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1731
|
-
const { $from, empty, ranges } = selection;
|
|
1732
|
-
if (!dispatch) {
|
|
1733
|
-
return true;
|
|
1734
|
-
}
|
|
1735
|
-
if (empty && extendEmptyMarkRange) {
|
|
1736
|
-
let { from, to } = selection;
|
|
1737
|
-
const attrs = (_a = $from.marks().find((mark) => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
|
|
1738
|
-
const range = getMarkRange($from, type, attrs);
|
|
1739
|
-
if (range) {
|
|
1740
|
-
from = range.from;
|
|
1741
|
-
to = range.to;
|
|
1742
|
-
}
|
|
1743
|
-
tr.removeMark(from, to, type);
|
|
1744
|
-
} else {
|
|
1745
|
-
ranges.forEach((range) => {
|
|
1746
|
-
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
|
1747
|
-
});
|
|
1748
|
-
}
|
|
1749
|
-
tr.removeStoredMark(type);
|
|
1750
|
-
return true;
|
|
1751
|
-
};
|
|
1752
|
-
updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1753
|
-
let nodeType = null;
|
|
1754
|
-
let markType = null;
|
|
1755
|
-
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
|
1756
|
-
if (!schemaType) {
|
|
1757
|
-
return false;
|
|
1758
|
-
}
|
|
1759
|
-
if (schemaType === "node") {
|
|
1760
|
-
nodeType = getNodeType(typeOrName, state.schema);
|
|
1761
|
-
}
|
|
1762
|
-
if (schemaType === "mark") {
|
|
1763
|
-
markType = getMarkType(typeOrName, state.schema);
|
|
1764
|
-
}
|
|
1765
|
-
if (dispatch) {
|
|
1766
|
-
tr.selection.ranges.forEach((range) => {
|
|
1767
|
-
const from = range.$from.pos;
|
|
1768
|
-
const to = range.$to.pos;
|
|
1769
|
-
let lastPos;
|
|
1770
|
-
let lastNode;
|
|
1771
|
-
let trimmedFrom;
|
|
1772
|
-
let trimmedTo;
|
|
1773
|
-
if (tr.selection.empty) {
|
|
1774
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1775
|
-
if (nodeType && nodeType === node.type) {
|
|
1776
|
-
trimmedFrom = Math.max(pos, from);
|
|
1777
|
-
trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
1778
|
-
lastPos = pos;
|
|
1779
|
-
lastNode = node;
|
|
1780
|
-
}
|
|
1781
|
-
});
|
|
1782
|
-
} else {
|
|
1783
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1784
|
-
if (pos < from && nodeType && nodeType === node.type) {
|
|
1785
|
-
trimmedFrom = Math.max(pos, from);
|
|
1786
|
-
trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
1787
|
-
lastPos = pos;
|
|
1788
|
-
lastNode = node;
|
|
1789
|
-
}
|
|
1790
|
-
if (pos >= from && pos <= to) {
|
|
1791
|
-
if (nodeType && nodeType === node.type) {
|
|
1792
|
-
tr.setNodeMarkup(pos, void 0, {
|
|
1793
|
-
...node.attrs,
|
|
1794
|
-
...attributes
|
|
1795
|
-
});
|
|
1796
|
-
}
|
|
1797
|
-
if (markType && node.marks.length) {
|
|
1798
|
-
node.marks.forEach((mark) => {
|
|
1799
|
-
if (markType === mark.type) {
|
|
1800
|
-
const trimmedFrom2 = Math.max(pos, from);
|
|
1801
|
-
const trimmedTo2 = Math.min(pos + node.nodeSize, to);
|
|
1802
|
-
tr.addMark(trimmedFrom2, trimmedTo2, markType.create({
|
|
1803
|
-
...mark.attrs,
|
|
1804
|
-
...attributes
|
|
1805
|
-
}));
|
|
1806
|
-
}
|
|
1807
|
-
});
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
});
|
|
1811
|
-
}
|
|
1812
|
-
if (lastNode) {
|
|
1813
|
-
if (lastPos !== void 0) {
|
|
1814
|
-
tr.setNodeMarkup(lastPos, void 0, {
|
|
1815
|
-
...lastNode.attrs,
|
|
1816
|
-
...attributes
|
|
1817
|
-
});
|
|
1818
|
-
}
|
|
1819
|
-
if (markType && lastNode.marks.length) {
|
|
1820
|
-
lastNode.marks.forEach((mark) => {
|
|
1821
|
-
if (markType === mark.type) {
|
|
1822
|
-
tr.addMark(trimmedFrom, trimmedTo, markType.create({
|
|
1823
|
-
...mark.attrs,
|
|
1824
|
-
...attributes
|
|
1825
|
-
}));
|
|
1826
|
-
}
|
|
1827
|
-
});
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
});
|
|
1831
|
-
}
|
|
1832
|
-
return true;
|
|
1833
|
-
};
|
|
1834
|
-
wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1835
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1836
|
-
return wrapIn$1(type, attributes)(state, dispatch);
|
|
1837
|
-
};
|
|
1838
|
-
wrapInList = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1839
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1840
|
-
return wrapInList$1(type, attributes)(state, dispatch);
|
|
1841
|
-
};
|
|
1842
|
-
commands = /* @__PURE__ */ Object.freeze({
|
|
1843
|
-
__proto__: null,
|
|
1844
|
-
blur,
|
|
1845
|
-
clearContent,
|
|
1846
|
-
clearNodes,
|
|
1847
|
-
command,
|
|
1848
|
-
createParagraphNear,
|
|
1849
|
-
cut,
|
|
1850
|
-
deleteCurrentNode,
|
|
1851
|
-
deleteNode,
|
|
1852
|
-
deleteRange,
|
|
1853
|
-
deleteSelection,
|
|
1854
|
-
enter,
|
|
1855
|
-
exitCode,
|
|
1856
|
-
extendMarkRange,
|
|
1857
|
-
first,
|
|
1858
|
-
focus,
|
|
1859
|
-
forEach,
|
|
1860
|
-
insertContent,
|
|
1861
|
-
insertContentAt,
|
|
1862
|
-
joinBackward,
|
|
1863
|
-
joinDown,
|
|
1864
|
-
joinForward,
|
|
1865
|
-
joinItemBackward,
|
|
1866
|
-
joinItemForward,
|
|
1867
|
-
joinTextblockBackward,
|
|
1868
|
-
joinTextblockForward,
|
|
1869
|
-
joinUp,
|
|
1870
|
-
keyboardShortcut,
|
|
1871
|
-
lift,
|
|
1872
|
-
liftEmptyBlock,
|
|
1873
|
-
liftListItem,
|
|
1874
|
-
newlineInCode,
|
|
1875
|
-
resetAttributes,
|
|
1876
|
-
scrollIntoView,
|
|
1877
|
-
selectAll,
|
|
1878
|
-
selectNodeBackward,
|
|
1879
|
-
selectNodeForward,
|
|
1880
|
-
selectParentNode,
|
|
1881
|
-
selectTextblockEnd,
|
|
1882
|
-
selectTextblockStart,
|
|
1883
|
-
setContent,
|
|
1884
|
-
setMark,
|
|
1885
|
-
setMeta,
|
|
1886
|
-
setNode,
|
|
1887
|
-
setNodeSelection,
|
|
1888
|
-
setTextSelection,
|
|
1889
|
-
sinkListItem,
|
|
1890
|
-
splitBlock,
|
|
1891
|
-
splitListItem,
|
|
1892
|
-
toggleList,
|
|
1893
|
-
toggleMark,
|
|
1894
|
-
toggleNode,
|
|
1895
|
-
toggleWrap,
|
|
1896
|
-
undoInputRule,
|
|
1897
|
-
unsetAllMarks,
|
|
1898
|
-
unsetMark,
|
|
1899
|
-
updateAttributes,
|
|
1900
|
-
wrapIn,
|
|
1901
|
-
wrapInList
|
|
1902
|
-
});
|
|
1903
|
-
Commands = Extension.create({
|
|
1904
|
-
name: "commands",
|
|
1905
|
-
addCommands() {
|
|
1906
|
-
return {
|
|
1907
|
-
...commands
|
|
1908
|
-
};
|
|
1909
|
-
}
|
|
1910
|
-
});
|
|
1911
|
-
Drop = Extension.create({
|
|
1912
|
-
name: "drop",
|
|
1913
|
-
addProseMirrorPlugins() {
|
|
1914
|
-
return [
|
|
1915
|
-
new Plugin({
|
|
1916
|
-
key: new PluginKey("tiptapDrop"),
|
|
1917
|
-
props: {
|
|
1918
|
-
handleDrop: (_, e, slice, moved) => {
|
|
1919
|
-
this.editor.emit("drop", {
|
|
1920
|
-
editor: this.editor,
|
|
1921
|
-
event: e,
|
|
1922
|
-
slice,
|
|
1923
|
-
moved
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
})
|
|
1928
|
-
];
|
|
1929
|
-
}
|
|
1930
|
-
});
|
|
1931
|
-
Editable = Extension.create({
|
|
1932
|
-
name: "editable",
|
|
1933
|
-
addProseMirrorPlugins() {
|
|
1934
|
-
return [
|
|
1935
|
-
new Plugin({
|
|
1936
|
-
key: new PluginKey("editable"),
|
|
1937
|
-
props: {
|
|
1938
|
-
editable: () => this.editor.options.editable
|
|
1939
|
-
}
|
|
1940
|
-
})
|
|
1941
|
-
];
|
|
1942
|
-
}
|
|
1943
|
-
});
|
|
1944
|
-
focusEventsPluginKey = new PluginKey("focusEvents");
|
|
1945
|
-
FocusEvents = Extension.create({
|
|
1946
|
-
name: "focusEvents",
|
|
1947
|
-
addProseMirrorPlugins() {
|
|
1948
|
-
const { editor } = this;
|
|
1949
|
-
return [
|
|
1950
|
-
new Plugin({
|
|
1951
|
-
key: focusEventsPluginKey,
|
|
1952
|
-
props: {
|
|
1953
|
-
handleDOMEvents: {
|
|
1954
|
-
focus: (view, event) => {
|
|
1955
|
-
editor.isFocused = true;
|
|
1956
|
-
const transaction = editor.state.tr.setMeta("focus", { event }).setMeta("addToHistory", false);
|
|
1957
|
-
view.dispatch(transaction);
|
|
1958
|
-
return false;
|
|
1959
|
-
},
|
|
1960
|
-
blur: (view, event) => {
|
|
1961
|
-
editor.isFocused = false;
|
|
1962
|
-
const transaction = editor.state.tr.setMeta("blur", { event }).setMeta("addToHistory", false);
|
|
1963
|
-
view.dispatch(transaction);
|
|
1964
|
-
return false;
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
})
|
|
1969
|
-
];
|
|
1970
|
-
}
|
|
1971
|
-
});
|
|
1972
|
-
Keymap = Extension.create({
|
|
1973
|
-
name: "keymap",
|
|
1974
|
-
addKeyboardShortcuts() {
|
|
1975
|
-
const handleBackspace = () => this.editor.commands.first(({ commands: commands2 }) => [
|
|
1976
|
-
() => commands2.undoInputRule(),
|
|
1977
|
-
// maybe convert first text block node to default node
|
|
1978
|
-
() => commands2.command(({ tr }) => {
|
|
1979
|
-
const { selection, doc } = tr;
|
|
1980
|
-
const { empty, $anchor } = selection;
|
|
1981
|
-
const { pos, parent } = $anchor;
|
|
1982
|
-
const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor;
|
|
1983
|
-
const parentIsIsolating = $parentPos.parent.type.spec.isolating;
|
|
1984
|
-
const parentPos = $anchor.pos - $anchor.parentOffset;
|
|
1985
|
-
const isAtStart = parentIsIsolating && $parentPos.parent.childCount === 1 ? parentPos === $anchor.pos : Selection.atStart(doc).from === pos;
|
|
1986
|
-
if (!empty || !parent.type.isTextblock || parent.textContent.length || !isAtStart || isAtStart && $anchor.parent.type.name === "paragraph") {
|
|
1987
|
-
return false;
|
|
1988
|
-
}
|
|
1989
|
-
return commands2.clearNodes();
|
|
1990
|
-
}),
|
|
1991
|
-
() => commands2.deleteSelection(),
|
|
1992
|
-
() => commands2.joinBackward(),
|
|
1993
|
-
() => commands2.selectNodeBackward()
|
|
1994
|
-
]);
|
|
1995
|
-
const handleDelete = () => this.editor.commands.first(({ commands: commands2 }) => [
|
|
1996
|
-
() => commands2.deleteSelection(),
|
|
1997
|
-
() => commands2.deleteCurrentNode(),
|
|
1998
|
-
() => commands2.joinForward(),
|
|
1999
|
-
() => commands2.selectNodeForward()
|
|
2000
|
-
]);
|
|
2001
|
-
const handleEnter = () => this.editor.commands.first(({ commands: commands2 }) => [
|
|
2002
|
-
() => commands2.newlineInCode(),
|
|
2003
|
-
() => commands2.createParagraphNear(),
|
|
2004
|
-
() => commands2.liftEmptyBlock(),
|
|
2005
|
-
() => commands2.splitBlock()
|
|
2006
|
-
]);
|
|
2007
|
-
const baseKeymap = {
|
|
2008
|
-
Enter: handleEnter,
|
|
2009
|
-
"Mod-Enter": () => this.editor.commands.exitCode(),
|
|
2010
|
-
Backspace: handleBackspace,
|
|
2011
|
-
"Mod-Backspace": handleBackspace,
|
|
2012
|
-
"Shift-Backspace": handleBackspace,
|
|
2013
|
-
Delete: handleDelete,
|
|
2014
|
-
"Mod-Delete": handleDelete,
|
|
2015
|
-
"Mod-a": () => this.editor.commands.selectAll()
|
|
2016
|
-
};
|
|
2017
|
-
const pcKeymap = {
|
|
2018
|
-
...baseKeymap
|
|
2019
|
-
};
|
|
2020
|
-
const macKeymap = {
|
|
2021
|
-
...baseKeymap,
|
|
2022
|
-
"Ctrl-h": handleBackspace,
|
|
2023
|
-
"Alt-Backspace": handleBackspace,
|
|
2024
|
-
"Ctrl-d": handleDelete,
|
|
2025
|
-
"Ctrl-Alt-Backspace": handleDelete,
|
|
2026
|
-
"Alt-Delete": handleDelete,
|
|
2027
|
-
"Alt-d": handleDelete,
|
|
2028
|
-
"Ctrl-a": () => this.editor.commands.selectTextblockStart(),
|
|
2029
|
-
"Ctrl-e": () => this.editor.commands.selectTextblockEnd()
|
|
2030
|
-
};
|
|
2031
|
-
if (isiOS() || isMacOS()) {
|
|
2032
|
-
return macKeymap;
|
|
2033
|
-
}
|
|
2034
|
-
return pcKeymap;
|
|
2035
|
-
},
|
|
2036
|
-
addProseMirrorPlugins() {
|
|
2037
|
-
return [
|
|
2038
|
-
// With this plugin we check if the whole document was selected and deleted.
|
|
2039
|
-
// In this case we will additionally call `clearNodes()` to convert e.g. a heading
|
|
2040
|
-
// to a paragraph if necessary.
|
|
2041
|
-
// This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
|
|
2042
|
-
// with many other commands.
|
|
2043
|
-
new Plugin({
|
|
2044
|
-
key: new PluginKey("clearDocument"),
|
|
2045
|
-
appendTransaction: (transactions, oldState, newState) => {
|
|
2046
|
-
if (transactions.some((tr2) => tr2.getMeta("composition"))) {
|
|
2047
|
-
return;
|
|
2048
|
-
}
|
|
2049
|
-
const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc);
|
|
2050
|
-
const ignoreTr = transactions.some((transaction) => transaction.getMeta("preventClearDocument"));
|
|
2051
|
-
if (!docChanges || ignoreTr) {
|
|
2052
|
-
return;
|
|
2053
|
-
}
|
|
2054
|
-
const { empty, from, to } = oldState.selection;
|
|
2055
|
-
const allFrom = Selection.atStart(oldState.doc).from;
|
|
2056
|
-
const allEnd = Selection.atEnd(oldState.doc).to;
|
|
2057
|
-
const allWasSelected = from === allFrom && to === allEnd;
|
|
2058
|
-
if (empty || !allWasSelected) {
|
|
2059
|
-
return;
|
|
2060
|
-
}
|
|
2061
|
-
const isEmpty = isNodeEmpty(newState.doc);
|
|
2062
|
-
if (!isEmpty) {
|
|
2063
|
-
return;
|
|
2064
|
-
}
|
|
2065
|
-
const tr = newState.tr;
|
|
2066
|
-
const state = createChainableState({
|
|
2067
|
-
state: newState,
|
|
2068
|
-
transaction: tr
|
|
2069
|
-
});
|
|
2070
|
-
const { commands: commands2 } = new CommandManager({
|
|
2071
|
-
editor: this.editor,
|
|
2072
|
-
state
|
|
2073
|
-
});
|
|
2074
|
-
commands2.clearNodes();
|
|
2075
|
-
if (!tr.steps.length) {
|
|
2076
|
-
return;
|
|
2077
|
-
}
|
|
2078
|
-
return tr;
|
|
2079
|
-
}
|
|
2080
|
-
})
|
|
2081
|
-
];
|
|
2082
|
-
}
|
|
2083
|
-
});
|
|
2084
|
-
Paste = Extension.create({
|
|
2085
|
-
name: "paste",
|
|
2086
|
-
addProseMirrorPlugins() {
|
|
2087
|
-
return [
|
|
2088
|
-
new Plugin({
|
|
2089
|
-
key: new PluginKey("tiptapPaste"),
|
|
2090
|
-
props: {
|
|
2091
|
-
handlePaste: (_view, e, slice) => {
|
|
2092
|
-
this.editor.emit("paste", {
|
|
2093
|
-
editor: this.editor,
|
|
2094
|
-
event: e,
|
|
2095
|
-
slice
|
|
2096
|
-
});
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
})
|
|
2100
|
-
];
|
|
2101
|
-
}
|
|
2102
|
-
});
|
|
2103
|
-
Tabindex = Extension.create({
|
|
2104
|
-
name: "tabindex",
|
|
2105
|
-
addProseMirrorPlugins() {
|
|
2106
|
-
return [
|
|
2107
|
-
new Plugin({
|
|
2108
|
-
key: new PluginKey("tabindex"),
|
|
2109
|
-
props: {
|
|
2110
|
-
attributes: () => this.editor.isEditable ? { tabindex: "0" } : {}
|
|
2111
|
-
}
|
|
2112
|
-
})
|
|
2113
|
-
];
|
|
2114
|
-
}
|
|
2115
|
-
});
|
|
2116
|
-
Node = class _Node {
|
|
2117
|
-
constructor(config = {}) {
|
|
2118
|
-
this.type = "node";
|
|
2119
|
-
this.name = "node";
|
|
2120
|
-
this.parent = null;
|
|
2121
|
-
this.child = null;
|
|
2122
|
-
this.config = {
|
|
2123
|
-
name: this.name,
|
|
2124
|
-
defaultOptions: {}
|
|
2125
|
-
};
|
|
2126
|
-
this.config = {
|
|
2127
|
-
...this.config,
|
|
2128
|
-
...config
|
|
2129
|
-
};
|
|
2130
|
-
this.name = this.config.name;
|
|
2131
|
-
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
2132
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
2133
|
-
}
|
|
2134
|
-
this.options = this.config.defaultOptions;
|
|
2135
|
-
if (this.config.addOptions) {
|
|
2136
|
-
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
|
2137
|
-
name: this.name
|
|
2138
|
-
}));
|
|
2139
|
-
}
|
|
2140
|
-
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
|
2141
|
-
name: this.name,
|
|
2142
|
-
options: this.options
|
|
2143
|
-
})) || {};
|
|
2144
|
-
}
|
|
2145
|
-
static create(config = {}) {
|
|
2146
|
-
return new _Node(config);
|
|
2147
|
-
}
|
|
2148
|
-
configure(options = {}) {
|
|
2149
|
-
const extension = this.extend({
|
|
2150
|
-
...this.config,
|
|
2151
|
-
addOptions: () => {
|
|
2152
|
-
return mergeDeep(this.options, options);
|
|
2153
|
-
}
|
|
2154
|
-
});
|
|
2155
|
-
extension.name = this.name;
|
|
2156
|
-
extension.parent = this.parent;
|
|
2157
|
-
return extension;
|
|
2158
|
-
}
|
|
2159
|
-
extend(extendedConfig = {}) {
|
|
2160
|
-
const extension = new _Node(extendedConfig);
|
|
2161
|
-
extension.parent = this;
|
|
2162
|
-
this.child = extension;
|
|
2163
|
-
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
2164
|
-
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
|
2165
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
2166
|
-
}
|
|
2167
|
-
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
|
2168
|
-
name: extension.name
|
|
2169
|
-
}));
|
|
2170
|
-
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
|
2171
|
-
name: extension.name,
|
|
2172
|
-
options: extension.options
|
|
2173
|
-
}));
|
|
2174
|
-
return extension;
|
|
2175
|
-
}
|
|
2176
|
-
};
|
|
2177
|
-
}
|
|
2178
|
-
});
|
|
2179
|
-
|
|
2180
42
|
// src/extensions/FontSize.ts
|
|
43
|
+
import { Extension } from "@tiptap/core";
|
|
2181
44
|
var FontSize;
|
|
2182
45
|
var init_FontSize = __esm({
|
|
2183
46
|
"src/extensions/FontSize.ts"() {
|
|
2184
47
|
"use strict";
|
|
2185
|
-
init_dist();
|
|
2186
48
|
FontSize = Extension.create({
|
|
2187
49
|
name: "fontSize",
|
|
2188
50
|
addOptions() {
|
|
@@ -2226,12 +88,12 @@ var init_FontSize = __esm({
|
|
|
2226
88
|
});
|
|
2227
89
|
|
|
2228
90
|
// src/extensions/LineHeight.ts
|
|
91
|
+
import { Extension as Extension2 } from "@tiptap/core";
|
|
2229
92
|
var LineHeight;
|
|
2230
93
|
var init_LineHeight = __esm({
|
|
2231
94
|
"src/extensions/LineHeight.ts"() {
|
|
2232
95
|
"use strict";
|
|
2233
|
-
|
|
2234
|
-
LineHeight = Extension.create({
|
|
96
|
+
LineHeight = Extension2.create({
|
|
2235
97
|
name: "lineHeight",
|
|
2236
98
|
addOptions() {
|
|
2237
99
|
return {
|
|
@@ -2262,14 +124,14 @@ var init_LineHeight = __esm({
|
|
|
2262
124
|
},
|
|
2263
125
|
addCommands() {
|
|
2264
126
|
return {
|
|
2265
|
-
setLineHeight: (lineHeight) => ({ commands
|
|
127
|
+
setLineHeight: (lineHeight) => ({ commands }) => {
|
|
2266
128
|
return this.options.types.every(
|
|
2267
|
-
(type) =>
|
|
129
|
+
(type) => commands.updateAttributes(type, { lineHeight })
|
|
2268
130
|
);
|
|
2269
131
|
},
|
|
2270
|
-
unsetLineHeight: () => ({ commands
|
|
132
|
+
unsetLineHeight: () => ({ commands }) => {
|
|
2271
133
|
return this.options.types.every(
|
|
2272
|
-
(type) =>
|
|
134
|
+
(type) => commands.resetAttributes(type, "lineHeight")
|
|
2273
135
|
);
|
|
2274
136
|
}
|
|
2275
137
|
};
|
|
@@ -2279,11 +141,11 @@ var init_LineHeight = __esm({
|
|
|
2279
141
|
});
|
|
2280
142
|
|
|
2281
143
|
// src/extensions/Video.ts
|
|
144
|
+
import { Node, mergeAttributes } from "@tiptap/core";
|
|
2282
145
|
var Video;
|
|
2283
146
|
var init_Video = __esm({
|
|
2284
147
|
"src/extensions/Video.ts"() {
|
|
2285
148
|
"use strict";
|
|
2286
|
-
init_dist();
|
|
2287
149
|
Video = Node.create({
|
|
2288
150
|
name: "video",
|
|
2289
151
|
addOptions() {
|
|
@@ -2338,8 +200,8 @@ var init_Video = __esm({
|
|
|
2338
200
|
},
|
|
2339
201
|
addCommands() {
|
|
2340
202
|
return {
|
|
2341
|
-
setVideo: (options) => ({ commands
|
|
2342
|
-
return
|
|
203
|
+
setVideo: (options) => ({ commands }) => {
|
|
204
|
+
return commands.insertContent({
|
|
2343
205
|
type: this.name,
|
|
2344
206
|
attrs: options
|
|
2345
207
|
});
|
|
@@ -2351,11 +213,11 @@ var init_Video = __esm({
|
|
|
2351
213
|
});
|
|
2352
214
|
|
|
2353
215
|
// src/extensions/Emoji.ts
|
|
216
|
+
import { Extension as Extension3 } from "@tiptap/core";
|
|
2354
217
|
var EMOJI_CATEGORIES, Emoji;
|
|
2355
218
|
var init_Emoji = __esm({
|
|
2356
219
|
"src/extensions/Emoji.ts"() {
|
|
2357
220
|
"use strict";
|
|
2358
|
-
init_dist();
|
|
2359
221
|
EMOJI_CATEGORIES = {
|
|
2360
222
|
smileys: {
|
|
2361
223
|
label: "Smileys & People",
|
|
@@ -3364,7 +1226,7 @@ var init_Emoji = __esm({
|
|
|
3364
1226
|
]
|
|
3365
1227
|
}
|
|
3366
1228
|
};
|
|
3367
|
-
Emoji =
|
|
1229
|
+
Emoji = Extension3.create({
|
|
3368
1230
|
name: "emoji",
|
|
3369
1231
|
addOptions() {
|
|
3370
1232
|
return {
|
|
@@ -3373,8 +1235,8 @@ var init_Emoji = __esm({
|
|
|
3373
1235
|
},
|
|
3374
1236
|
addCommands() {
|
|
3375
1237
|
return {
|
|
3376
|
-
insertEmoji: (emoji) => ({ commands
|
|
3377
|
-
return
|
|
1238
|
+
insertEmoji: (emoji) => ({ commands }) => {
|
|
1239
|
+
return commands.insertContent(emoji);
|
|
3378
1240
|
}
|
|
3379
1241
|
};
|
|
3380
1242
|
}
|
|
@@ -3383,12 +1245,12 @@ var init_Emoji = __esm({
|
|
|
3383
1245
|
});
|
|
3384
1246
|
|
|
3385
1247
|
// src/extensions/Fullscreen.ts
|
|
1248
|
+
import { Extension as Extension4 } from "@tiptap/core";
|
|
3386
1249
|
var Fullscreen;
|
|
3387
1250
|
var init_Fullscreen = __esm({
|
|
3388
1251
|
"src/extensions/Fullscreen.ts"() {
|
|
3389
1252
|
"use strict";
|
|
3390
|
-
|
|
3391
|
-
Fullscreen = Extension.create({
|
|
1253
|
+
Fullscreen = Extension4.create({
|
|
3392
1254
|
name: "fullscreen",
|
|
3393
1255
|
addOptions() {
|
|
3394
1256
|
return {
|
|
@@ -3454,11 +1316,11 @@ var init_Fullscreen = __esm({
|
|
|
3454
1316
|
});
|
|
3455
1317
|
|
|
3456
1318
|
// src/extensions/Print.ts
|
|
1319
|
+
import { Extension as Extension5 } from "@tiptap/core";
|
|
3457
1320
|
var defaultPrintStyles, Print;
|
|
3458
1321
|
var init_Print = __esm({
|
|
3459
1322
|
"src/extensions/Print.ts"() {
|
|
3460
1323
|
"use strict";
|
|
3461
|
-
init_dist();
|
|
3462
1324
|
defaultPrintStyles = `
|
|
3463
1325
|
body {
|
|
3464
1326
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
@@ -3574,7 +1436,7 @@ var init_Print = __esm({
|
|
|
3574
1436
|
}
|
|
3575
1437
|
}
|
|
3576
1438
|
`;
|
|
3577
|
-
Print =
|
|
1439
|
+
Print = Extension5.create({
|
|
3578
1440
|
name: "print",
|
|
3579
1441
|
addOptions() {
|
|
3580
1442
|
return {
|
|
@@ -3632,12 +1494,12 @@ var init_Print = __esm({
|
|
|
3632
1494
|
});
|
|
3633
1495
|
|
|
3634
1496
|
// src/extensions/Indent.ts
|
|
1497
|
+
import { Extension as Extension6 } from "@tiptap/core";
|
|
3635
1498
|
var Indent;
|
|
3636
1499
|
var init_Indent = __esm({
|
|
3637
1500
|
"src/extensions/Indent.ts"() {
|
|
3638
1501
|
"use strict";
|
|
3639
|
-
|
|
3640
|
-
Indent = Extension.create({
|
|
1502
|
+
Indent = Extension6.create({
|
|
3641
1503
|
name: "indent",
|
|
3642
1504
|
addOptions() {
|
|
3643
1505
|
return {
|
|
@@ -4884,9 +2746,9 @@ var init_SlateToolbar = __esm({
|
|
|
4884
2746
|
onToggleFullscreen,
|
|
4885
2747
|
onPrint,
|
|
4886
2748
|
isFullscreen = false,
|
|
4887
|
-
toggleMark:
|
|
2749
|
+
toggleMark: toggleMark2,
|
|
4888
2750
|
toggleBlock: toggleBlock2,
|
|
4889
|
-
isMarkActive:
|
|
2751
|
+
isMarkActive: isMarkActive2,
|
|
4890
2752
|
isBlockActive: isBlockActive2
|
|
4891
2753
|
}) => {
|
|
4892
2754
|
const [showEmojiPicker, setShowEmojiPicker] = useState2(false);
|
|
@@ -5020,8 +2882,8 @@ var init_SlateToolbar = __esm({
|
|
|
5020
2882
|
return /* @__PURE__ */ jsx3(
|
|
5021
2883
|
ToolbarButton,
|
|
5022
2884
|
{
|
|
5023
|
-
active:
|
|
5024
|
-
onClick: () =>
|
|
2885
|
+
active: isMarkActive2(editor, "bold"),
|
|
2886
|
+
onClick: () => toggleMark2(editor, "bold"),
|
|
5025
2887
|
title: "Bold (Ctrl+B)",
|
|
5026
2888
|
children: icons.bold
|
|
5027
2889
|
},
|
|
@@ -5031,8 +2893,8 @@ var init_SlateToolbar = __esm({
|
|
|
5031
2893
|
return /* @__PURE__ */ jsx3(
|
|
5032
2894
|
ToolbarButton,
|
|
5033
2895
|
{
|
|
5034
|
-
active:
|
|
5035
|
-
onClick: () =>
|
|
2896
|
+
active: isMarkActive2(editor, "italic"),
|
|
2897
|
+
onClick: () => toggleMark2(editor, "italic"),
|
|
5036
2898
|
title: "Italic (Ctrl+I)",
|
|
5037
2899
|
children: icons.italic
|
|
5038
2900
|
},
|
|
@@ -5042,8 +2904,8 @@ var init_SlateToolbar = __esm({
|
|
|
5042
2904
|
return /* @__PURE__ */ jsx3(
|
|
5043
2905
|
ToolbarButton,
|
|
5044
2906
|
{
|
|
5045
|
-
active:
|
|
5046
|
-
onClick: () =>
|
|
2907
|
+
active: isMarkActive2(editor, "underline"),
|
|
2908
|
+
onClick: () => toggleMark2(editor, "underline"),
|
|
5047
2909
|
title: "Underline (Ctrl+U)",
|
|
5048
2910
|
children: icons.underline
|
|
5049
2911
|
},
|
|
@@ -5053,8 +2915,8 @@ var init_SlateToolbar = __esm({
|
|
|
5053
2915
|
return /* @__PURE__ */ jsx3(
|
|
5054
2916
|
ToolbarButton,
|
|
5055
2917
|
{
|
|
5056
|
-
active:
|
|
5057
|
-
onClick: () =>
|
|
2918
|
+
active: isMarkActive2(editor, "strikethrough"),
|
|
2919
|
+
onClick: () => toggleMark2(editor, "strikethrough"),
|
|
5058
2920
|
title: "Strikethrough",
|
|
5059
2921
|
children: icons.strike
|
|
5060
2922
|
},
|
|
@@ -5064,8 +2926,8 @@ var init_SlateToolbar = __esm({
|
|
|
5064
2926
|
return /* @__PURE__ */ jsx3(
|
|
5065
2927
|
ToolbarButton,
|
|
5066
2928
|
{
|
|
5067
|
-
active:
|
|
5068
|
-
onClick: () =>
|
|
2929
|
+
active: isMarkActive2(editor, "code"),
|
|
2930
|
+
onClick: () => toggleMark2(editor, "code"),
|
|
5069
2931
|
title: "Code",
|
|
5070
2932
|
children: icons.code
|
|
5071
2933
|
},
|
|
@@ -5086,8 +2948,8 @@ var init_SlateToolbar = __esm({
|
|
|
5086
2948
|
return /* @__PURE__ */ jsx3(
|
|
5087
2949
|
ToolbarButton,
|
|
5088
2950
|
{
|
|
5089
|
-
active:
|
|
5090
|
-
onClick: () =>
|
|
2951
|
+
active: isMarkActive2(editor, "subscript"),
|
|
2952
|
+
onClick: () => toggleMark2(editor, "subscript"),
|
|
5091
2953
|
title: "Subscript",
|
|
5092
2954
|
children: icons.subscript
|
|
5093
2955
|
},
|
|
@@ -5097,8 +2959,8 @@ var init_SlateToolbar = __esm({
|
|
|
5097
2959
|
return /* @__PURE__ */ jsx3(
|
|
5098
2960
|
ToolbarButton,
|
|
5099
2961
|
{
|
|
5100
|
-
active:
|
|
5101
|
-
onClick: () =>
|
|
2962
|
+
active: isMarkActive2(editor, "superscript"),
|
|
2963
|
+
onClick: () => toggleMark2(editor, "superscript"),
|
|
5102
2964
|
title: "Superscript",
|
|
5103
2965
|
children: icons.superscript
|
|
5104
2966
|
},
|
|
@@ -5479,10 +3341,10 @@ import {
|
|
|
5479
3341
|
useRef as useRef3
|
|
5480
3342
|
} from "react";
|
|
5481
3343
|
import { createEditor, Editor as Editor2, Transforms as Transforms2, Text as Text2, Element as SlateElement2, Node as Node2 } from "slate";
|
|
5482
|
-
import { Slate, Editable
|
|
3344
|
+
import { Slate, Editable, withReact, ReactEditor as ReactEditor2 } from "slate-react";
|
|
5483
3345
|
import { withHistory, HistoryEditor } from "slate-history";
|
|
5484
3346
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
5485
|
-
var import_is_hotkey, HOTKEYS, LIST_TYPES, TEXT_ALIGN_TYPES,
|
|
3347
|
+
var import_is_hotkey, HOTKEYS, LIST_TYPES, TEXT_ALIGN_TYPES, isMarkActive, isBlockActive, toggleMark, toggleBlock, serializeToHtml, serializeNode, escapeHtml, deserializeFromHtml, deserializeElement, renderElement, renderLeaf, withInlines, SlateEditorComponent, SlateEditorComponent_default;
|
|
5486
3348
|
var init_SlateEditorComponent = __esm({
|
|
5487
3349
|
"src/adapters/slate/SlateEditorComponent.tsx"() {
|
|
5488
3350
|
"use strict";
|
|
@@ -5497,7 +3359,7 @@ var init_SlateEditorComponent = __esm({
|
|
|
5497
3359
|
};
|
|
5498
3360
|
LIST_TYPES = ["numbered-list", "bulleted-list"];
|
|
5499
3361
|
TEXT_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
5500
|
-
|
|
3362
|
+
isMarkActive = (editor, format) => {
|
|
5501
3363
|
const marks = Editor2.marks(editor);
|
|
5502
3364
|
return marks ? marks[format] === true : false;
|
|
5503
3365
|
};
|
|
@@ -5512,8 +3374,8 @@ var init_SlateEditorComponent = __esm({
|
|
|
5512
3374
|
);
|
|
5513
3375
|
return !!match;
|
|
5514
3376
|
};
|
|
5515
|
-
|
|
5516
|
-
const isActive =
|
|
3377
|
+
toggleMark = (editor, format) => {
|
|
3378
|
+
const isActive = isMarkActive(editor, format);
|
|
5517
3379
|
if (isActive) {
|
|
5518
3380
|
Editor2.removeMark(editor, format);
|
|
5519
3381
|
} else {
|
|
@@ -5526,7 +3388,7 @@ var init_SlateEditorComponent = __esm({
|
|
|
5526
3388
|
format,
|
|
5527
3389
|
TEXT_ALIGN_TYPES.includes(format) ? "align" : "type"
|
|
5528
3390
|
);
|
|
5529
|
-
const
|
|
3391
|
+
const isList = LIST_TYPES.includes(format);
|
|
5530
3392
|
Transforms2.unwrapNodes(editor, {
|
|
5531
3393
|
match: (n) => !Editor2.isEditor(n) && SlateElement2.isElement(n) && LIST_TYPES.includes(n.type) && !TEXT_ALIGN_TYPES.includes(format),
|
|
5532
3394
|
split: true
|
|
@@ -5538,11 +3400,11 @@ var init_SlateEditorComponent = __esm({
|
|
|
5538
3400
|
};
|
|
5539
3401
|
} else {
|
|
5540
3402
|
newProperties = {
|
|
5541
|
-
type: isActive ? "paragraph" :
|
|
3403
|
+
type: isActive ? "paragraph" : isList ? "list-item" : format
|
|
5542
3404
|
};
|
|
5543
3405
|
}
|
|
5544
3406
|
Transforms2.setNodes(editor, newProperties);
|
|
5545
|
-
if (!isActive &&
|
|
3407
|
+
if (!isActive && isList) {
|
|
5546
3408
|
const block = { type: format, children: [] };
|
|
5547
3409
|
Transforms2.wrapNodes(editor, block);
|
|
5548
3410
|
}
|
|
@@ -5829,7 +3691,7 @@ var init_SlateEditorComponent = __esm({
|
|
|
5829
3691
|
if ((0, import_is_hotkey.default)(hotkey, event)) {
|
|
5830
3692
|
event.preventDefault();
|
|
5831
3693
|
const mark = HOTKEYS[hotkey];
|
|
5832
|
-
|
|
3694
|
+
toggleMark(editor, mark);
|
|
5833
3695
|
}
|
|
5834
3696
|
}
|
|
5835
3697
|
},
|
|
@@ -5963,9 +3825,9 @@ var init_SlateEditorComponent = __esm({
|
|
|
5963
3825
|
onToggleFullscreen: handleToggleFullscreen,
|
|
5964
3826
|
onPrint: handlePrint,
|
|
5965
3827
|
isFullscreen,
|
|
5966
|
-
toggleMark
|
|
3828
|
+
toggleMark,
|
|
5967
3829
|
toggleBlock,
|
|
5968
|
-
isMarkActive
|
|
3830
|
+
isMarkActive,
|
|
5969
3831
|
isBlockActive
|
|
5970
3832
|
}
|
|
5971
3833
|
),
|
|
@@ -5980,7 +3842,7 @@ var init_SlateEditorComponent = __esm({
|
|
|
5980
3842
|
overflow: "auto"
|
|
5981
3843
|
},
|
|
5982
3844
|
children: /* @__PURE__ */ jsx4(
|
|
5983
|
-
|
|
3845
|
+
Editable,
|
|
5984
3846
|
{
|
|
5985
3847
|
className: "rte-builder-content",
|
|
5986
3848
|
renderElement,
|
|
@@ -6589,7 +4451,7 @@ import {
|
|
|
6589
4451
|
COMMAND_PRIORITY_CRITICAL
|
|
6590
4452
|
} from "lexical";
|
|
6591
4453
|
import { $generateHtmlFromNodes, $generateNodesFromDOM } from "@lexical/html";
|
|
6592
|
-
import { Fragment
|
|
4454
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
6593
4455
|
function onError(error) {
|
|
6594
4456
|
console.error("Lexical Error:", error);
|
|
6595
4457
|
}
|
|
@@ -6861,7 +4723,7 @@ var init_LexicalEditorComponent = __esm({
|
|
|
6861
4723
|
getNativeEditor: () => editor
|
|
6862
4724
|
}));
|
|
6863
4725
|
const characterLimit = charCounterMax > 0 ? charCounterMax : null;
|
|
6864
|
-
return /* @__PURE__ */ jsxs6(
|
|
4726
|
+
return /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
6865
4727
|
/* @__PURE__ */ jsx6(
|
|
6866
4728
|
LexicalToolbar,
|
|
6867
4729
|
{
|
|
@@ -8674,8 +6536,1381 @@ init_Emoji();
|
|
|
8674
6536
|
init_Fullscreen();
|
|
8675
6537
|
init_Print();
|
|
8676
6538
|
init_Indent();
|
|
6539
|
+
|
|
6540
|
+
// src/collaboration/CollaborationContext.tsx
|
|
6541
|
+
import { createContext, useContext, useReducer, useCallback as useCallback7, useEffect as useEffect9, useRef as useRef8 } from "react";
|
|
6542
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
6543
|
+
var initialState = {
|
|
6544
|
+
status: "disconnected",
|
|
6545
|
+
users: [],
|
|
6546
|
+
error: void 0,
|
|
6547
|
+
isHost: false
|
|
6548
|
+
};
|
|
6549
|
+
function collaborationReducer(state, action) {
|
|
6550
|
+
switch (action.type) {
|
|
6551
|
+
case "SET_STATUS":
|
|
6552
|
+
return { ...state, status: action.status, error: action.status === "error" ? state.error : void 0 };
|
|
6553
|
+
case "SET_USERS":
|
|
6554
|
+
return { ...state, users: action.users };
|
|
6555
|
+
case "ADD_USER":
|
|
6556
|
+
if (state.users.find((u) => u.id === action.user.id)) {
|
|
6557
|
+
return {
|
|
6558
|
+
...state,
|
|
6559
|
+
users: state.users.map((u) => u.id === action.user.id ? action.user : u)
|
|
6560
|
+
};
|
|
6561
|
+
}
|
|
6562
|
+
return { ...state, users: [...state.users, action.user] };
|
|
6563
|
+
case "REMOVE_USER":
|
|
6564
|
+
return { ...state, users: state.users.filter((u) => u.id !== action.userId) };
|
|
6565
|
+
case "UPDATE_CURSOR":
|
|
6566
|
+
return {
|
|
6567
|
+
...state,
|
|
6568
|
+
users: state.users.map(
|
|
6569
|
+
(u) => u.id === action.userId ? { ...u, cursor: action.cursor, lastActive: Date.now() } : u
|
|
6570
|
+
)
|
|
6571
|
+
};
|
|
6572
|
+
case "SET_ERROR":
|
|
6573
|
+
return { ...state, status: "error", error: action.error };
|
|
6574
|
+
case "CLEAR_ERROR":
|
|
6575
|
+
return { ...state, error: void 0 };
|
|
6576
|
+
case "SET_HOST":
|
|
6577
|
+
return { ...state, isHost: action.isHost };
|
|
6578
|
+
default:
|
|
6579
|
+
return state;
|
|
6580
|
+
}
|
|
6581
|
+
}
|
|
6582
|
+
var CollaborationContext = createContext(null);
|
|
6583
|
+
function generateUserColor() {
|
|
6584
|
+
const colors = [
|
|
6585
|
+
"#f87171",
|
|
6586
|
+
"#fb923c",
|
|
6587
|
+
"#fbbf24",
|
|
6588
|
+
"#a3e635",
|
|
6589
|
+
"#4ade80",
|
|
6590
|
+
"#2dd4bf",
|
|
6591
|
+
"#22d3ee",
|
|
6592
|
+
"#60a5fa",
|
|
6593
|
+
"#a78bfa",
|
|
6594
|
+
"#e879f9"
|
|
6595
|
+
];
|
|
6596
|
+
return colors[Math.floor(Math.random() * colors.length)];
|
|
6597
|
+
}
|
|
6598
|
+
function CollaborationProvider({
|
|
6599
|
+
children,
|
|
6600
|
+
config,
|
|
6601
|
+
onStatusChange,
|
|
6602
|
+
onUsersChange
|
|
6603
|
+
}) {
|
|
6604
|
+
const [state, dispatch] = useReducer(collaborationReducer, initialState);
|
|
6605
|
+
const wsRef = useRef8(null);
|
|
6606
|
+
const reconnectTimeoutRef = useRef8(null);
|
|
6607
|
+
const reconnectAttemptsRef = useRef8(0);
|
|
6608
|
+
useEffect9(() => {
|
|
6609
|
+
onStatusChange?.(state.status);
|
|
6610
|
+
}, [state.status, onStatusChange]);
|
|
6611
|
+
useEffect9(() => {
|
|
6612
|
+
onUsersChange?.(state.users);
|
|
6613
|
+
}, [state.users, onUsersChange]);
|
|
6614
|
+
const connect = useCallback7(async () => {
|
|
6615
|
+
if (!config) return;
|
|
6616
|
+
dispatch({ type: "SET_STATUS", status: "connecting" });
|
|
6617
|
+
try {
|
|
6618
|
+
if (config.provider === "websocket" && config.serverUrl) {
|
|
6619
|
+
const url = new URL(config.serverUrl);
|
|
6620
|
+
url.searchParams.set("room", config.roomId);
|
|
6621
|
+
if (config.token) {
|
|
6622
|
+
url.searchParams.set("token", config.token);
|
|
6623
|
+
}
|
|
6624
|
+
const ws = new WebSocket(url.toString());
|
|
6625
|
+
wsRef.current = ws;
|
|
6626
|
+
ws.onopen = () => {
|
|
6627
|
+
dispatch({ type: "SET_STATUS", status: "connected" });
|
|
6628
|
+
reconnectAttemptsRef.current = 0;
|
|
6629
|
+
ws.send(JSON.stringify({
|
|
6630
|
+
type: "join",
|
|
6631
|
+
user: {
|
|
6632
|
+
id: config.user.id,
|
|
6633
|
+
name: config.user.name,
|
|
6634
|
+
avatar: config.user.avatar,
|
|
6635
|
+
color: config.user.color || generateUserColor()
|
|
6636
|
+
}
|
|
6637
|
+
}));
|
|
6638
|
+
};
|
|
6639
|
+
ws.onmessage = (event) => {
|
|
6640
|
+
try {
|
|
6641
|
+
const message = JSON.parse(event.data);
|
|
6642
|
+
handleCollaborationEvent(message);
|
|
6643
|
+
} catch {
|
|
6644
|
+
console.error("Failed to parse collaboration message");
|
|
6645
|
+
}
|
|
6646
|
+
};
|
|
6647
|
+
ws.onclose = () => {
|
|
6648
|
+
dispatch({ type: "SET_STATUS", status: "disconnected" });
|
|
6649
|
+
handleReconnect();
|
|
6650
|
+
};
|
|
6651
|
+
ws.onerror = () => {
|
|
6652
|
+
dispatch({ type: "SET_ERROR", error: "Connection error" });
|
|
6653
|
+
};
|
|
6654
|
+
} else if (config.provider === "webrtc") {
|
|
6655
|
+
dispatch({ type: "SET_ERROR", error: "WebRTC provider not yet implemented" });
|
|
6656
|
+
} else if (config.provider === "custom") {
|
|
6657
|
+
dispatch({ type: "SET_STATUS", status: "connected" });
|
|
6658
|
+
}
|
|
6659
|
+
} catch (error) {
|
|
6660
|
+
dispatch({ type: "SET_ERROR", error: error instanceof Error ? error.message : "Connection failed" });
|
|
6661
|
+
}
|
|
6662
|
+
}, [config]);
|
|
6663
|
+
const handleCollaborationEvent = useCallback7((event) => {
|
|
6664
|
+
switch (event.type) {
|
|
6665
|
+
case "user-joined":
|
|
6666
|
+
dispatch({ type: "ADD_USER", user: event.user });
|
|
6667
|
+
break;
|
|
6668
|
+
case "user-left":
|
|
6669
|
+
dispatch({ type: "REMOVE_USER", userId: event.userId });
|
|
6670
|
+
break;
|
|
6671
|
+
case "cursor-moved":
|
|
6672
|
+
dispatch({ type: "UPDATE_CURSOR", userId: event.userId, cursor: event.cursor });
|
|
6673
|
+
break;
|
|
6674
|
+
case "status-changed":
|
|
6675
|
+
dispatch({ type: "SET_STATUS", status: event.status });
|
|
6676
|
+
break;
|
|
6677
|
+
case "error":
|
|
6678
|
+
dispatch({ type: "SET_ERROR", error: event.message });
|
|
6679
|
+
break;
|
|
6680
|
+
}
|
|
6681
|
+
}, []);
|
|
6682
|
+
const handleReconnect = useCallback7(() => {
|
|
6683
|
+
if (!config?.autoReconnect) return;
|
|
6684
|
+
const maxAttempts = config.maxReconnectAttempts ?? 5;
|
|
6685
|
+
const interval = config.reconnectInterval ?? 3e3;
|
|
6686
|
+
if (reconnectAttemptsRef.current >= maxAttempts) {
|
|
6687
|
+
dispatch({ type: "SET_ERROR", error: "Max reconnection attempts reached" });
|
|
6688
|
+
return;
|
|
6689
|
+
}
|
|
6690
|
+
dispatch({ type: "SET_STATUS", status: "reconnecting" });
|
|
6691
|
+
reconnectAttemptsRef.current++;
|
|
6692
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
6693
|
+
connect();
|
|
6694
|
+
}, interval);
|
|
6695
|
+
}, [config, connect]);
|
|
6696
|
+
const disconnect = useCallback7(() => {
|
|
6697
|
+
if (reconnectTimeoutRef.current) {
|
|
6698
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
6699
|
+
}
|
|
6700
|
+
if (wsRef.current) {
|
|
6701
|
+
wsRef.current.close();
|
|
6702
|
+
wsRef.current = null;
|
|
6703
|
+
}
|
|
6704
|
+
dispatch({ type: "SET_STATUS", status: "disconnected" });
|
|
6705
|
+
dispatch({ type: "SET_USERS", users: [] });
|
|
6706
|
+
}, []);
|
|
6707
|
+
const updateCursor = useCallback7((cursor) => {
|
|
6708
|
+
if (!config || !wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
|
|
6709
|
+
wsRef.current.send(JSON.stringify({
|
|
6710
|
+
type: "cursor",
|
|
6711
|
+
userId: config.user.id,
|
|
6712
|
+
cursor
|
|
6713
|
+
}));
|
|
6714
|
+
}, [config]);
|
|
6715
|
+
useEffect9(() => {
|
|
6716
|
+
return () => {
|
|
6717
|
+
disconnect();
|
|
6718
|
+
};
|
|
6719
|
+
}, [disconnect]);
|
|
6720
|
+
const value = {
|
|
6721
|
+
state,
|
|
6722
|
+
config: config ?? null,
|
|
6723
|
+
connect,
|
|
6724
|
+
disconnect,
|
|
6725
|
+
updateCursor,
|
|
6726
|
+
isEnabled: !!config
|
|
6727
|
+
};
|
|
6728
|
+
return /* @__PURE__ */ jsx10(CollaborationContext.Provider, { value, children });
|
|
6729
|
+
}
|
|
6730
|
+
function useCollaboration() {
|
|
6731
|
+
const context = useContext(CollaborationContext);
|
|
6732
|
+
if (!context) {
|
|
6733
|
+
throw new Error("useCollaboration must be used within a CollaborationProvider");
|
|
6734
|
+
}
|
|
6735
|
+
return context;
|
|
6736
|
+
}
|
|
6737
|
+
function useCollaborationOptional() {
|
|
6738
|
+
return useContext(CollaborationContext);
|
|
6739
|
+
}
|
|
6740
|
+
|
|
6741
|
+
// src/collaboration/PresenceIndicator.tsx
|
|
6742
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
6743
|
+
function getInitials(name) {
|
|
6744
|
+
return name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
|
|
6745
|
+
}
|
|
6746
|
+
function UserAvatar({ user, showName }) {
|
|
6747
|
+
return /* @__PURE__ */ jsxs10(
|
|
6748
|
+
"div",
|
|
6749
|
+
{
|
|
6750
|
+
className: "rte-presence-avatar",
|
|
6751
|
+
style: { borderColor: user.color },
|
|
6752
|
+
title: user.name,
|
|
6753
|
+
children: [
|
|
6754
|
+
user.avatar ? /* @__PURE__ */ jsx11("img", { src: user.avatar, alt: user.name }) : /* @__PURE__ */ jsx11("span", { style: { backgroundColor: user.color }, children: getInitials(user.name) }),
|
|
6755
|
+
user.isActive && /* @__PURE__ */ jsx11("div", { className: "rte-presence-active-dot" }),
|
|
6756
|
+
showName && /* @__PURE__ */ jsx11("div", { className: "rte-presence-name", children: user.name })
|
|
6757
|
+
]
|
|
6758
|
+
}
|
|
6759
|
+
);
|
|
6760
|
+
}
|
|
6761
|
+
function StatusBadge({ status }) {
|
|
6762
|
+
const statusConfig = {
|
|
6763
|
+
connected: { label: "Connected", color: "#22c55e" },
|
|
6764
|
+
connecting: { label: "Connecting...", color: "#eab308" },
|
|
6765
|
+
reconnecting: { label: "Reconnecting...", color: "#f97316" },
|
|
6766
|
+
disconnected: { label: "Disconnected", color: "#6b7280" },
|
|
6767
|
+
error: { label: "Error", color: "#ef4444" }
|
|
6768
|
+
};
|
|
6769
|
+
const config = statusConfig[status] || statusConfig.disconnected;
|
|
6770
|
+
return /* @__PURE__ */ jsxs10("div", { className: "rte-presence-status", style: { color: config.color }, children: [
|
|
6771
|
+
/* @__PURE__ */ jsx11("div", { className: "rte-presence-status-dot", style: { backgroundColor: config.color } }),
|
|
6772
|
+
config.label
|
|
6773
|
+
] });
|
|
6774
|
+
}
|
|
6775
|
+
function PresenceIndicator({
|
|
6776
|
+
maxAvatars = 5,
|
|
6777
|
+
showNames = false,
|
|
6778
|
+
className = ""
|
|
6779
|
+
}) {
|
|
6780
|
+
const collaboration = useCollaborationOptional();
|
|
6781
|
+
if (!collaboration?.isEnabled) {
|
|
6782
|
+
return null;
|
|
6783
|
+
}
|
|
6784
|
+
const { state } = collaboration;
|
|
6785
|
+
const visibleUsers = state.users.slice(0, maxAvatars);
|
|
6786
|
+
const remainingCount = Math.max(0, state.users.length - maxAvatars);
|
|
6787
|
+
return /* @__PURE__ */ jsxs10("div", { className: `rte-presence-indicator ${className}`, children: [
|
|
6788
|
+
/* @__PURE__ */ jsx11(StatusBadge, { status: state.status }),
|
|
6789
|
+
state.users.length > 0 && /* @__PURE__ */ jsxs10("div", { className: "rte-presence-avatars", children: [
|
|
6790
|
+
visibleUsers.map((user) => /* @__PURE__ */ jsx11(UserAvatar, { user, showName: showNames }, user.id)),
|
|
6791
|
+
remainingCount > 0 && /* @__PURE__ */ jsxs10("div", { className: "rte-presence-avatar rte-presence-more", children: [
|
|
6792
|
+
"+",
|
|
6793
|
+
remainingCount
|
|
6794
|
+
] })
|
|
6795
|
+
] }),
|
|
6796
|
+
state.error && /* @__PURE__ */ jsx11("div", { className: "rte-presence-error", title: state.error, children: "!" })
|
|
6797
|
+
] });
|
|
6798
|
+
}
|
|
6799
|
+
|
|
6800
|
+
// src/comments/types.ts
|
|
6801
|
+
var DEFAULT_REACTION_EMOJIS = ["\u{1F44D}", "\u{1F44E}", "\u2764\uFE0F", "\u{1F389}", "\u{1F604}", "\u{1F615}", "\u{1F440}", "\u{1F680}"];
|
|
6802
|
+
|
|
6803
|
+
// src/comments/CommentsContext.tsx
|
|
6804
|
+
import { createContext as createContext2, useContext as useContext2, useReducer as useReducer2, useCallback as useCallback8, useEffect as useEffect10 } from "react";
|
|
6805
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
6806
|
+
var initialState2 = {
|
|
6807
|
+
threads: [],
|
|
6808
|
+
activeThreadId: null,
|
|
6809
|
+
isPanelOpen: false,
|
|
6810
|
+
filter: "all",
|
|
6811
|
+
currentUser: null
|
|
6812
|
+
};
|
|
6813
|
+
function generateId() {
|
|
6814
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
6815
|
+
}
|
|
6816
|
+
function commentsReducer(state, action) {
|
|
6817
|
+
switch (action.type) {
|
|
6818
|
+
case "SET_THREADS":
|
|
6819
|
+
return { ...state, threads: action.threads };
|
|
6820
|
+
case "ADD_THREAD":
|
|
6821
|
+
return { ...state, threads: [...state.threads, action.thread] };
|
|
6822
|
+
case "DELETE_THREAD":
|
|
6823
|
+
return {
|
|
6824
|
+
...state,
|
|
6825
|
+
threads: state.threads.filter((t) => t.id !== action.threadId),
|
|
6826
|
+
activeThreadId: state.activeThreadId === action.threadId ? null : state.activeThreadId
|
|
6827
|
+
};
|
|
6828
|
+
case "RESOLVE_THREAD":
|
|
6829
|
+
return {
|
|
6830
|
+
...state,
|
|
6831
|
+
threads: state.threads.map(
|
|
6832
|
+
(t) => t.id === action.threadId ? { ...t, status: "resolved", resolvedAt: Date.now(), resolvedBy: action.resolvedBy } : t
|
|
6833
|
+
)
|
|
6834
|
+
};
|
|
6835
|
+
case "REOPEN_THREAD":
|
|
6836
|
+
return {
|
|
6837
|
+
...state,
|
|
6838
|
+
threads: state.threads.map(
|
|
6839
|
+
(t) => t.id === action.threadId ? { ...t, status: "open", resolvedAt: void 0, resolvedBy: void 0 } : t
|
|
6840
|
+
)
|
|
6841
|
+
};
|
|
6842
|
+
case "ADD_COMMENT":
|
|
6843
|
+
return {
|
|
6844
|
+
...state,
|
|
6845
|
+
threads: state.threads.map(
|
|
6846
|
+
(t) => t.id === action.threadId ? { ...t, comments: [...t.comments, action.comment] } : t
|
|
6847
|
+
)
|
|
6848
|
+
};
|
|
6849
|
+
case "UPDATE_COMMENT":
|
|
6850
|
+
return {
|
|
6851
|
+
...state,
|
|
6852
|
+
threads: state.threads.map(
|
|
6853
|
+
(t) => t.id === action.threadId ? {
|
|
6854
|
+
...t,
|
|
6855
|
+
comments: t.comments.map(
|
|
6856
|
+
(c) => c.id === action.comment.id ? action.comment : c
|
|
6857
|
+
)
|
|
6858
|
+
} : t
|
|
6859
|
+
)
|
|
6860
|
+
};
|
|
6861
|
+
case "DELETE_COMMENT":
|
|
6862
|
+
return {
|
|
6863
|
+
...state,
|
|
6864
|
+
threads: state.threads.map(
|
|
6865
|
+
(t) => t.id === action.threadId ? { ...t, comments: t.comments.filter((c) => c.id !== action.commentId) } : t
|
|
6866
|
+
)
|
|
6867
|
+
};
|
|
6868
|
+
case "ADD_REACTION":
|
|
6869
|
+
return {
|
|
6870
|
+
...state,
|
|
6871
|
+
threads: state.threads.map(
|
|
6872
|
+
(t) => t.id === action.threadId ? {
|
|
6873
|
+
...t,
|
|
6874
|
+
comments: t.comments.map((c) => {
|
|
6875
|
+
if (c.id !== action.commentId) return c;
|
|
6876
|
+
const reactions = c.reactions || [];
|
|
6877
|
+
const existingReaction = reactions.find((r) => r.emoji === action.emoji);
|
|
6878
|
+
if (existingReaction) {
|
|
6879
|
+
return {
|
|
6880
|
+
...c,
|
|
6881
|
+
reactions: reactions.map(
|
|
6882
|
+
(r) => r.emoji === action.emoji ? { ...r, users: [...r.users, action.user] } : r
|
|
6883
|
+
)
|
|
6884
|
+
};
|
|
6885
|
+
}
|
|
6886
|
+
return {
|
|
6887
|
+
...c,
|
|
6888
|
+
reactions: [...reactions, { emoji: action.emoji, users: [action.user] }]
|
|
6889
|
+
};
|
|
6890
|
+
})
|
|
6891
|
+
} : t
|
|
6892
|
+
)
|
|
6893
|
+
};
|
|
6894
|
+
case "REMOVE_REACTION":
|
|
6895
|
+
return {
|
|
6896
|
+
...state,
|
|
6897
|
+
threads: state.threads.map(
|
|
6898
|
+
(t) => t.id === action.threadId ? {
|
|
6899
|
+
...t,
|
|
6900
|
+
comments: t.comments.map((c) => {
|
|
6901
|
+
if (c.id !== action.commentId) return c;
|
|
6902
|
+
return {
|
|
6903
|
+
...c,
|
|
6904
|
+
reactions: (c.reactions || []).map(
|
|
6905
|
+
(r) => r.emoji === action.emoji ? { ...r, users: r.users.filter((u) => u.id !== action.userId) } : r
|
|
6906
|
+
).filter((r) => r.users.length > 0)
|
|
6907
|
+
};
|
|
6908
|
+
})
|
|
6909
|
+
} : t
|
|
6910
|
+
)
|
|
6911
|
+
};
|
|
6912
|
+
case "SET_ACTIVE_THREAD":
|
|
6913
|
+
return { ...state, activeThreadId: action.threadId };
|
|
6914
|
+
case "TOGGLE_PANEL":
|
|
6915
|
+
return { ...state, isPanelOpen: action.isOpen ?? !state.isPanelOpen };
|
|
6916
|
+
case "SET_FILTER":
|
|
6917
|
+
return { ...state, filter: action.filter };
|
|
6918
|
+
case "SET_CURRENT_USER":
|
|
6919
|
+
return { ...state, currentUser: action.user };
|
|
6920
|
+
default:
|
|
6921
|
+
return state;
|
|
6922
|
+
}
|
|
6923
|
+
}
|
|
6924
|
+
var CommentsContext = createContext2(null);
|
|
6925
|
+
function CommentsProvider({
|
|
6926
|
+
children,
|
|
6927
|
+
config,
|
|
6928
|
+
initialThreads = [],
|
|
6929
|
+
onThreadsChange
|
|
6930
|
+
}) {
|
|
6931
|
+
const [state, dispatch] = useReducer2(commentsReducer, {
|
|
6932
|
+
...initialState2,
|
|
6933
|
+
threads: initialThreads,
|
|
6934
|
+
currentUser: config?.currentUser ?? null
|
|
6935
|
+
});
|
|
6936
|
+
const subscribersRef = { current: /* @__PURE__ */ new Set() };
|
|
6937
|
+
useEffect10(() => {
|
|
6938
|
+
if (config?.onLoad) {
|
|
6939
|
+
config.onLoad().then((threads) => {
|
|
6940
|
+
dispatch({ type: "SET_THREADS", threads });
|
|
6941
|
+
});
|
|
6942
|
+
}
|
|
6943
|
+
}, [config]);
|
|
6944
|
+
useEffect10(() => {
|
|
6945
|
+
onThreadsChange?.(state.threads);
|
|
6946
|
+
if (config?.onSave) {
|
|
6947
|
+
config.onSave(state.threads);
|
|
6948
|
+
}
|
|
6949
|
+
}, [state.threads, onThreadsChange, config]);
|
|
6950
|
+
useEffect10(() => {
|
|
6951
|
+
if (config?.currentUser) {
|
|
6952
|
+
dispatch({ type: "SET_CURRENT_USER", user: config.currentUser });
|
|
6953
|
+
}
|
|
6954
|
+
}, [config?.currentUser]);
|
|
6955
|
+
const emitEvent = useCallback8((event) => {
|
|
6956
|
+
subscribersRef.current.forEach((callback) => callback(event));
|
|
6957
|
+
}, []);
|
|
6958
|
+
const createThread = useCallback8((range, initialComment) => {
|
|
6959
|
+
if (!state.currentUser) return null;
|
|
6960
|
+
const threadId = generateId();
|
|
6961
|
+
const commentId = generateId();
|
|
6962
|
+
const now = Date.now();
|
|
6963
|
+
const comment = {
|
|
6964
|
+
id: commentId,
|
|
6965
|
+
threadId,
|
|
6966
|
+
content: initialComment,
|
|
6967
|
+
author: state.currentUser,
|
|
6968
|
+
createdAt: now,
|
|
6969
|
+
isEdited: false
|
|
6970
|
+
};
|
|
6971
|
+
const thread = {
|
|
6972
|
+
id: threadId,
|
|
6973
|
+
range,
|
|
6974
|
+
comments: [comment],
|
|
6975
|
+
status: "open",
|
|
6976
|
+
createdAt: now
|
|
6977
|
+
};
|
|
6978
|
+
dispatch({ type: "ADD_THREAD", thread });
|
|
6979
|
+
emitEvent({ type: "thread-created", thread });
|
|
6980
|
+
return thread;
|
|
6981
|
+
}, [state.currentUser, emitEvent]);
|
|
6982
|
+
const deleteThread = useCallback8((threadId) => {
|
|
6983
|
+
if (config?.allowDelete === false) return;
|
|
6984
|
+
dispatch({ type: "DELETE_THREAD", threadId });
|
|
6985
|
+
emitEvent({ type: "thread-deleted", threadId });
|
|
6986
|
+
}, [config, emitEvent]);
|
|
6987
|
+
const resolveThread = useCallback8((threadId) => {
|
|
6988
|
+
if (!state.currentUser || config?.allowResolve === false) return;
|
|
6989
|
+
dispatch({ type: "RESOLVE_THREAD", threadId, resolvedBy: state.currentUser });
|
|
6990
|
+
emitEvent({ type: "thread-resolved", threadId, resolvedBy: state.currentUser });
|
|
6991
|
+
}, [state.currentUser, config, emitEvent]);
|
|
6992
|
+
const reopenThread = useCallback8((threadId) => {
|
|
6993
|
+
dispatch({ type: "REOPEN_THREAD", threadId });
|
|
6994
|
+
emitEvent({ type: "thread-reopened", threadId });
|
|
6995
|
+
}, [emitEvent]);
|
|
6996
|
+
const addComment = useCallback8((threadId, content) => {
|
|
6997
|
+
if (!state.currentUser) return null;
|
|
6998
|
+
const comment = {
|
|
6999
|
+
id: generateId(),
|
|
7000
|
+
threadId,
|
|
7001
|
+
content,
|
|
7002
|
+
author: state.currentUser,
|
|
7003
|
+
createdAt: Date.now(),
|
|
7004
|
+
isEdited: false
|
|
7005
|
+
};
|
|
7006
|
+
dispatch({ type: "ADD_COMMENT", threadId, comment });
|
|
7007
|
+
emitEvent({ type: "comment-added", threadId, comment });
|
|
7008
|
+
return comment;
|
|
7009
|
+
}, [state.currentUser, emitEvent]);
|
|
7010
|
+
const updateComment = useCallback8((threadId, commentId, content) => {
|
|
7011
|
+
if (config?.allowEdit === false) return;
|
|
7012
|
+
const thread = state.threads.find((t) => t.id === threadId);
|
|
7013
|
+
const existingComment = thread?.comments.find((c) => c.id === commentId);
|
|
7014
|
+
if (!existingComment) return;
|
|
7015
|
+
const updatedComment = {
|
|
7016
|
+
...existingComment,
|
|
7017
|
+
content,
|
|
7018
|
+
updatedAt: Date.now(),
|
|
7019
|
+
isEdited: true
|
|
7020
|
+
};
|
|
7021
|
+
dispatch({ type: "UPDATE_COMMENT", threadId, comment: updatedComment });
|
|
7022
|
+
emitEvent({ type: "comment-updated", threadId, comment: updatedComment });
|
|
7023
|
+
}, [state.threads, config, emitEvent]);
|
|
7024
|
+
const deleteComment = useCallback8((threadId, commentId) => {
|
|
7025
|
+
if (config?.allowDelete === false) return;
|
|
7026
|
+
dispatch({ type: "DELETE_COMMENT", threadId, commentId });
|
|
7027
|
+
emitEvent({ type: "comment-deleted", threadId, commentId });
|
|
7028
|
+
}, [config, emitEvent]);
|
|
7029
|
+
const addReaction = useCallback8((threadId, commentId, emoji) => {
|
|
7030
|
+
if (!state.currentUser || config?.allowReactions === false) return;
|
|
7031
|
+
const allowedEmojis = config?.reactionEmojis ?? DEFAULT_REACTION_EMOJIS;
|
|
7032
|
+
if (!allowedEmojis.includes(emoji)) return;
|
|
7033
|
+
dispatch({ type: "ADD_REACTION", threadId, commentId, emoji, user: state.currentUser });
|
|
7034
|
+
emitEvent({ type: "reaction-added", threadId, commentId, emoji, user: state.currentUser });
|
|
7035
|
+
}, [state.currentUser, config, emitEvent]);
|
|
7036
|
+
const removeReaction = useCallback8((threadId, commentId, emoji) => {
|
|
7037
|
+
if (!state.currentUser) return;
|
|
7038
|
+
dispatch({ type: "REMOVE_REACTION", threadId, commentId, emoji, userId: state.currentUser.id });
|
|
7039
|
+
emitEvent({ type: "reaction-removed", threadId, commentId, emoji, userId: state.currentUser.id });
|
|
7040
|
+
}, [state.currentUser, emitEvent]);
|
|
7041
|
+
const setActiveThread = useCallback8((threadId) => {
|
|
7042
|
+
dispatch({ type: "SET_ACTIVE_THREAD", threadId });
|
|
7043
|
+
}, []);
|
|
7044
|
+
const togglePanel = useCallback8((isOpen) => {
|
|
7045
|
+
dispatch({ type: "TOGGLE_PANEL", isOpen });
|
|
7046
|
+
}, []);
|
|
7047
|
+
const setFilter = useCallback8((filter) => {
|
|
7048
|
+
dispatch({ type: "SET_FILTER", filter });
|
|
7049
|
+
}, []);
|
|
7050
|
+
const getThreadByRange = useCallback8((from, to) => {
|
|
7051
|
+
return state.threads.find((t) => t.range.from === from && t.range.to === to);
|
|
7052
|
+
}, [state.threads]);
|
|
7053
|
+
const getFilteredThreads = useCallback8(() => {
|
|
7054
|
+
if (state.filter === "all") return state.threads;
|
|
7055
|
+
return state.threads.filter((t) => t.status === state.filter);
|
|
7056
|
+
}, [state.threads, state.filter]);
|
|
7057
|
+
const subscribe = useCallback8((callback) => {
|
|
7058
|
+
subscribersRef.current.add(callback);
|
|
7059
|
+
return () => {
|
|
7060
|
+
subscribersRef.current.delete(callback);
|
|
7061
|
+
};
|
|
7062
|
+
}, []);
|
|
7063
|
+
const value = {
|
|
7064
|
+
state,
|
|
7065
|
+
config: config ?? null,
|
|
7066
|
+
createThread,
|
|
7067
|
+
deleteThread,
|
|
7068
|
+
resolveThread,
|
|
7069
|
+
reopenThread,
|
|
7070
|
+
addComment,
|
|
7071
|
+
updateComment,
|
|
7072
|
+
deleteComment,
|
|
7073
|
+
addReaction,
|
|
7074
|
+
removeReaction,
|
|
7075
|
+
setActiveThread,
|
|
7076
|
+
togglePanel,
|
|
7077
|
+
setFilter,
|
|
7078
|
+
getThreadByRange,
|
|
7079
|
+
getFilteredThreads,
|
|
7080
|
+
subscribe,
|
|
7081
|
+
isEnabled: !!config
|
|
7082
|
+
};
|
|
7083
|
+
return /* @__PURE__ */ jsx12(CommentsContext.Provider, { value, children });
|
|
7084
|
+
}
|
|
7085
|
+
function useComments() {
|
|
7086
|
+
const context = useContext2(CommentsContext);
|
|
7087
|
+
if (!context) {
|
|
7088
|
+
throw new Error("useComments must be used within a CommentsProvider");
|
|
7089
|
+
}
|
|
7090
|
+
return context;
|
|
7091
|
+
}
|
|
7092
|
+
function useCommentsOptional() {
|
|
7093
|
+
return useContext2(CommentsContext);
|
|
7094
|
+
}
|
|
7095
|
+
|
|
7096
|
+
// src/comments/CommentsPanel.tsx
|
|
7097
|
+
import { useState as useState8 } from "react";
|
|
7098
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
7099
|
+
function formatRelativeTime(timestamp) {
|
|
7100
|
+
const now = Date.now();
|
|
7101
|
+
const diff = now - timestamp;
|
|
7102
|
+
const minutes = Math.floor(diff / 6e4);
|
|
7103
|
+
const hours = Math.floor(diff / 36e5);
|
|
7104
|
+
const days = Math.floor(diff / 864e5);
|
|
7105
|
+
if (minutes < 1) return "Just now";
|
|
7106
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
7107
|
+
if (hours < 24) return `${hours}h ago`;
|
|
7108
|
+
if (days < 7) return `${days}d ago`;
|
|
7109
|
+
return new Date(timestamp).toLocaleDateString();
|
|
7110
|
+
}
|
|
7111
|
+
function CommentItem({
|
|
7112
|
+
comment,
|
|
7113
|
+
threadId,
|
|
7114
|
+
isFirst
|
|
7115
|
+
}) {
|
|
7116
|
+
const comments = useCommentsOptional();
|
|
7117
|
+
const [isEditing, setIsEditing] = useState8(false);
|
|
7118
|
+
const [editContent, setEditContent] = useState8(comment.content);
|
|
7119
|
+
const [showReactions, setShowReactions] = useState8(false);
|
|
7120
|
+
if (!comments) return null;
|
|
7121
|
+
const { config, updateComment, deleteComment, addReaction, removeReaction, state } = comments;
|
|
7122
|
+
const canEdit = config?.allowEdit !== false && comment.author.id === state.currentUser?.id;
|
|
7123
|
+
const canDelete = config?.allowDelete !== false && comment.author.id === state.currentUser?.id;
|
|
7124
|
+
const canReact = config?.allowReactions !== false;
|
|
7125
|
+
const reactionEmojis = config?.reactionEmojis ?? DEFAULT_REACTION_EMOJIS;
|
|
7126
|
+
const handleSaveEdit = () => {
|
|
7127
|
+
updateComment(threadId, comment.id, editContent);
|
|
7128
|
+
setIsEditing(false);
|
|
7129
|
+
};
|
|
7130
|
+
const handleToggleReaction = (emoji) => {
|
|
7131
|
+
const reaction = comment.reactions?.find((r) => r.emoji === emoji);
|
|
7132
|
+
const hasReacted = reaction?.users.some((u) => u.id === state.currentUser?.id);
|
|
7133
|
+
if (hasReacted) {
|
|
7134
|
+
removeReaction(threadId, comment.id, emoji);
|
|
7135
|
+
} else {
|
|
7136
|
+
addReaction(threadId, comment.id, emoji);
|
|
7137
|
+
}
|
|
7138
|
+
setShowReactions(false);
|
|
7139
|
+
};
|
|
7140
|
+
return /* @__PURE__ */ jsxs11("div", { className: `rte-comment ${isFirst ? "rte-comment-first" : ""}`, children: [
|
|
7141
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-comment-header", children: [
|
|
7142
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-comment-author", children: [
|
|
7143
|
+
comment.author.avatar ? /* @__PURE__ */ jsx13("img", { src: comment.author.avatar, alt: comment.author.name, className: "rte-comment-avatar" }) : /* @__PURE__ */ jsx13("div", { className: "rte-comment-avatar-placeholder", children: comment.author.name.charAt(0).toUpperCase() }),
|
|
7144
|
+
/* @__PURE__ */ jsx13("span", { className: "rte-comment-author-name", children: comment.author.name })
|
|
7145
|
+
] }),
|
|
7146
|
+
/* @__PURE__ */ jsxs11("span", { className: "rte-comment-time", children: [
|
|
7147
|
+
formatRelativeTime(comment.createdAt),
|
|
7148
|
+
comment.isEdited && " (edited)"
|
|
7149
|
+
] })
|
|
7150
|
+
] }),
|
|
7151
|
+
isEditing ? /* @__PURE__ */ jsxs11("div", { className: "rte-comment-edit", children: [
|
|
7152
|
+
/* @__PURE__ */ jsx13(
|
|
7153
|
+
"textarea",
|
|
7154
|
+
{
|
|
7155
|
+
value: editContent,
|
|
7156
|
+
onChange: (e) => setEditContent(e.target.value),
|
|
7157
|
+
className: "rte-comment-edit-input"
|
|
7158
|
+
}
|
|
7159
|
+
),
|
|
7160
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-comment-edit-actions", children: [
|
|
7161
|
+
/* @__PURE__ */ jsx13("button", { onClick: handleSaveEdit, className: "rte-comment-btn-save", children: "Save" }),
|
|
7162
|
+
/* @__PURE__ */ jsx13("button", { onClick: () => setIsEditing(false), className: "rte-comment-btn-cancel", children: "Cancel" })
|
|
7163
|
+
] })
|
|
7164
|
+
] }) : /* @__PURE__ */ jsx13("div", { className: "rte-comment-content", dangerouslySetInnerHTML: { __html: comment.content } }),
|
|
7165
|
+
comment.reactions && comment.reactions.length > 0 && /* @__PURE__ */ jsx13("div", { className: "rte-comment-reactions", children: comment.reactions.map((reaction) => /* @__PURE__ */ jsxs11(
|
|
7166
|
+
"button",
|
|
7167
|
+
{
|
|
7168
|
+
className: `rte-comment-reaction ${reaction.users.some((u) => u.id === state.currentUser?.id) ? "active" : ""}`,
|
|
7169
|
+
onClick: () => handleToggleReaction(reaction.emoji),
|
|
7170
|
+
title: reaction.users.map((u) => u.name).join(", "),
|
|
7171
|
+
children: [
|
|
7172
|
+
reaction.emoji,
|
|
7173
|
+
" ",
|
|
7174
|
+
reaction.users.length
|
|
7175
|
+
]
|
|
7176
|
+
},
|
|
7177
|
+
reaction.emoji
|
|
7178
|
+
)) }),
|
|
7179
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-comment-actions", children: [
|
|
7180
|
+
canReact && /* @__PURE__ */ jsxs11("div", { className: "rte-comment-reaction-picker", children: [
|
|
7181
|
+
/* @__PURE__ */ jsx13(
|
|
7182
|
+
"button",
|
|
7183
|
+
{
|
|
7184
|
+
className: "rte-comment-action-btn",
|
|
7185
|
+
onClick: () => setShowReactions(!showReactions),
|
|
7186
|
+
children: "\u{1F60A}"
|
|
7187
|
+
}
|
|
7188
|
+
),
|
|
7189
|
+
showReactions && /* @__PURE__ */ jsx13("div", { className: "rte-comment-reaction-dropdown", children: reactionEmojis.map((emoji) => /* @__PURE__ */ jsx13("button", { onClick: () => handleToggleReaction(emoji), children: emoji }, emoji)) })
|
|
7190
|
+
] }),
|
|
7191
|
+
canEdit && /* @__PURE__ */ jsx13("button", { className: "rte-comment-action-btn", onClick: () => setIsEditing(true), children: "Edit" }),
|
|
7192
|
+
canDelete && !isFirst && /* @__PURE__ */ jsx13(
|
|
7193
|
+
"button",
|
|
7194
|
+
{
|
|
7195
|
+
className: "rte-comment-action-btn rte-comment-action-delete",
|
|
7196
|
+
onClick: () => deleteComment(threadId, comment.id),
|
|
7197
|
+
children: "Delete"
|
|
7198
|
+
}
|
|
7199
|
+
)
|
|
7200
|
+
] })
|
|
7201
|
+
] });
|
|
7202
|
+
}
|
|
7203
|
+
function ThreadItem({ thread }) {
|
|
7204
|
+
const comments = useCommentsOptional();
|
|
7205
|
+
const [replyContent, setReplyContent] = useState8("");
|
|
7206
|
+
const [showReply, setShowReply] = useState8(false);
|
|
7207
|
+
if (!comments) return null;
|
|
7208
|
+
const { state, setActiveThread, addComment, resolveThread, reopenThread, deleteThread, config } = comments;
|
|
7209
|
+
const isActive = state.activeThreadId === thread.id;
|
|
7210
|
+
const canResolve = config?.allowResolve !== false;
|
|
7211
|
+
const handleReply = () => {
|
|
7212
|
+
if (replyContent.trim()) {
|
|
7213
|
+
addComment(thread.id, replyContent);
|
|
7214
|
+
setReplyContent("");
|
|
7215
|
+
setShowReply(false);
|
|
7216
|
+
}
|
|
7217
|
+
};
|
|
7218
|
+
return /* @__PURE__ */ jsxs11(
|
|
7219
|
+
"div",
|
|
7220
|
+
{
|
|
7221
|
+
className: `rte-thread ${isActive ? "rte-thread-active" : ""} ${thread.status === "resolved" ? "rte-thread-resolved" : ""}`,
|
|
7222
|
+
onClick: () => setActiveThread(thread.id),
|
|
7223
|
+
children: [
|
|
7224
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-thread-header", children: [
|
|
7225
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-thread-quote", children: [
|
|
7226
|
+
'"',
|
|
7227
|
+
thread.range.text.slice(0, 50),
|
|
7228
|
+
'..."'
|
|
7229
|
+
] }),
|
|
7230
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-thread-meta", children: [
|
|
7231
|
+
/* @__PURE__ */ jsx13("span", { className: `rte-thread-status rte-thread-status-${thread.status}`, children: thread.status }),
|
|
7232
|
+
/* @__PURE__ */ jsxs11("span", { className: "rte-thread-count", children: [
|
|
7233
|
+
thread.comments.length,
|
|
7234
|
+
" comment",
|
|
7235
|
+
thread.comments.length !== 1 ? "s" : ""
|
|
7236
|
+
] })
|
|
7237
|
+
] })
|
|
7238
|
+
] }),
|
|
7239
|
+
/* @__PURE__ */ jsx13("div", { className: "rte-thread-comments", children: thread.comments.map((comment, index) => /* @__PURE__ */ jsx13(
|
|
7240
|
+
CommentItem,
|
|
7241
|
+
{
|
|
7242
|
+
comment,
|
|
7243
|
+
threadId: thread.id,
|
|
7244
|
+
isFirst: index === 0
|
|
7245
|
+
},
|
|
7246
|
+
comment.id
|
|
7247
|
+
)) }),
|
|
7248
|
+
thread.status === "open" && /* @__PURE__ */ jsx13("div", { className: "rte-thread-reply", children: showReply ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
7249
|
+
/* @__PURE__ */ jsx13(
|
|
7250
|
+
"textarea",
|
|
7251
|
+
{
|
|
7252
|
+
value: replyContent,
|
|
7253
|
+
onChange: (e) => setReplyContent(e.target.value),
|
|
7254
|
+
placeholder: "Write a reply...",
|
|
7255
|
+
className: "rte-thread-reply-input"
|
|
7256
|
+
}
|
|
7257
|
+
),
|
|
7258
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-thread-reply-actions", children: [
|
|
7259
|
+
/* @__PURE__ */ jsx13("button", { onClick: handleReply, className: "rte-btn-primary", disabled: !replyContent.trim(), children: "Reply" }),
|
|
7260
|
+
/* @__PURE__ */ jsx13("button", { onClick: () => setShowReply(false), className: "rte-btn-secondary", children: "Cancel" })
|
|
7261
|
+
] })
|
|
7262
|
+
] }) : /* @__PURE__ */ jsx13("button", { onClick: () => setShowReply(true), className: "rte-thread-reply-btn", children: "Reply" }) }),
|
|
7263
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-thread-actions", children: [
|
|
7264
|
+
canResolve && thread.status === "open" && /* @__PURE__ */ jsx13("button", { onClick: () => resolveThread(thread.id), className: "rte-btn-resolve", children: "\u2713 Resolve" }),
|
|
7265
|
+
thread.status === "resolved" && /* @__PURE__ */ jsx13("button", { onClick: () => reopenThread(thread.id), className: "rte-btn-reopen", children: "Reopen" }),
|
|
7266
|
+
/* @__PURE__ */ jsx13(
|
|
7267
|
+
"button",
|
|
7268
|
+
{
|
|
7269
|
+
onClick: (e) => {
|
|
7270
|
+
e.stopPropagation();
|
|
7271
|
+
deleteThread(thread.id);
|
|
7272
|
+
},
|
|
7273
|
+
className: "rte-btn-delete-thread",
|
|
7274
|
+
children: "Delete"
|
|
7275
|
+
}
|
|
7276
|
+
)
|
|
7277
|
+
] })
|
|
7278
|
+
]
|
|
7279
|
+
}
|
|
7280
|
+
);
|
|
7281
|
+
}
|
|
7282
|
+
function CommentsPanel({ position = "right", className = "" }) {
|
|
7283
|
+
const comments = useCommentsOptional();
|
|
7284
|
+
if (!comments?.isEnabled || !comments.state.isPanelOpen) {
|
|
7285
|
+
return null;
|
|
7286
|
+
}
|
|
7287
|
+
const { state, togglePanel, setFilter, getFilteredThreads } = comments;
|
|
7288
|
+
const filteredThreads = getFilteredThreads();
|
|
7289
|
+
return /* @__PURE__ */ jsxs11("div", { className: `rte-comments-panel rte-comments-panel-${position} ${className}`, children: [
|
|
7290
|
+
/* @__PURE__ */ jsxs11("div", { className: "rte-comments-panel-header", children: [
|
|
7291
|
+
/* @__PURE__ */ jsx13("h3", { children: "Comments" }),
|
|
7292
|
+
/* @__PURE__ */ jsx13("button", { onClick: () => togglePanel(false), className: "rte-comments-close-btn", children: "\xD7" })
|
|
7293
|
+
] }),
|
|
7294
|
+
/* @__PURE__ */ jsx13("div", { className: "rte-comments-filters", children: ["all", "open", "resolved"].map((filter) => /* @__PURE__ */ jsxs11(
|
|
7295
|
+
"button",
|
|
7296
|
+
{
|
|
7297
|
+
className: `rte-comments-filter ${state.filter === filter ? "active" : ""}`,
|
|
7298
|
+
onClick: () => setFilter(filter),
|
|
7299
|
+
children: [
|
|
7300
|
+
filter.charAt(0).toUpperCase() + filter.slice(1),
|
|
7301
|
+
/* @__PURE__ */ jsx13("span", { className: "rte-comments-filter-count", children: filter === "all" ? state.threads.length : state.threads.filter((t) => t.status === filter).length })
|
|
7302
|
+
]
|
|
7303
|
+
},
|
|
7304
|
+
filter
|
|
7305
|
+
)) }),
|
|
7306
|
+
/* @__PURE__ */ jsx13("div", { className: "rte-comments-list", children: filteredThreads.length === 0 ? /* @__PURE__ */ jsx13("div", { className: "rte-comments-empty", children: state.filter === "all" ? "No comments yet. Select text and add a comment." : `No ${state.filter} comments.` }) : filteredThreads.map((thread) => /* @__PURE__ */ jsx13(ThreadItem, { thread }, thread.id)) })
|
|
7307
|
+
] });
|
|
7308
|
+
}
|
|
7309
|
+
|
|
7310
|
+
// src/history/VersionHistoryContext.tsx
|
|
7311
|
+
import { createContext as createContext3, useContext as useContext3, useReducer as useReducer3, useCallback as useCallback9, useEffect as useEffect11, useRef as useRef9 } from "react";
|
|
7312
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
7313
|
+
var initialState3 = {
|
|
7314
|
+
versions: [],
|
|
7315
|
+
viewingVersionId: null,
|
|
7316
|
+
isPanelOpen: false,
|
|
7317
|
+
isComparing: false,
|
|
7318
|
+
compareFromId: null,
|
|
7319
|
+
compareToId: null,
|
|
7320
|
+
autoSaveEnabled: true,
|
|
7321
|
+
autoSaveInterval: 6e4
|
|
7322
|
+
};
|
|
7323
|
+
function generateId2() {
|
|
7324
|
+
return `v-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
7325
|
+
}
|
|
7326
|
+
function versionHistoryReducer(state, action) {
|
|
7327
|
+
switch (action.type) {
|
|
7328
|
+
case "SET_VERSIONS":
|
|
7329
|
+
return { ...state, versions: action.versions };
|
|
7330
|
+
case "ADD_VERSION":
|
|
7331
|
+
return {
|
|
7332
|
+
...state,
|
|
7333
|
+
versions: [action.version, ...state.versions]
|
|
7334
|
+
};
|
|
7335
|
+
case "DELETE_VERSION":
|
|
7336
|
+
return {
|
|
7337
|
+
...state,
|
|
7338
|
+
versions: state.versions.filter((v) => v.id !== action.versionId),
|
|
7339
|
+
viewingVersionId: state.viewingVersionId === action.versionId ? null : state.viewingVersionId
|
|
7340
|
+
};
|
|
7341
|
+
case "PIN_VERSION":
|
|
7342
|
+
return {
|
|
7343
|
+
...state,
|
|
7344
|
+
versions: state.versions.map(
|
|
7345
|
+
(v) => v.id === action.versionId ? { ...v, isPinned: true } : v
|
|
7346
|
+
)
|
|
7347
|
+
};
|
|
7348
|
+
case "UNPIN_VERSION":
|
|
7349
|
+
return {
|
|
7350
|
+
...state,
|
|
7351
|
+
versions: state.versions.map(
|
|
7352
|
+
(v) => v.id === action.versionId ? { ...v, isPinned: false } : v
|
|
7353
|
+
)
|
|
7354
|
+
};
|
|
7355
|
+
case "RENAME_VERSION":
|
|
7356
|
+
return {
|
|
7357
|
+
...state,
|
|
7358
|
+
versions: state.versions.map(
|
|
7359
|
+
(v) => v.id === action.versionId ? { ...v, title: action.title } : v
|
|
7360
|
+
)
|
|
7361
|
+
};
|
|
7362
|
+
case "SET_VIEWING_VERSION":
|
|
7363
|
+
return { ...state, viewingVersionId: action.versionId };
|
|
7364
|
+
case "TOGGLE_PANEL":
|
|
7365
|
+
return { ...state, isPanelOpen: action.isOpen ?? !state.isPanelOpen };
|
|
7366
|
+
case "START_COMPARE":
|
|
7367
|
+
return {
|
|
7368
|
+
...state,
|
|
7369
|
+
isComparing: true,
|
|
7370
|
+
compareFromId: action.fromId,
|
|
7371
|
+
compareToId: action.toId
|
|
7372
|
+
};
|
|
7373
|
+
case "STOP_COMPARE":
|
|
7374
|
+
return {
|
|
7375
|
+
...state,
|
|
7376
|
+
isComparing: false,
|
|
7377
|
+
compareFromId: null,
|
|
7378
|
+
compareToId: null
|
|
7379
|
+
};
|
|
7380
|
+
case "SET_AUTO_SAVE":
|
|
7381
|
+
return { ...state, autoSaveEnabled: action.enabled };
|
|
7382
|
+
case "SET_AUTO_SAVE_INTERVAL":
|
|
7383
|
+
return { ...state, autoSaveInterval: action.interval };
|
|
7384
|
+
default:
|
|
7385
|
+
return state;
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
function countWords(text) {
|
|
7389
|
+
return text.trim().split(/\s+/).filter(Boolean).length;
|
|
7390
|
+
}
|
|
7391
|
+
function computeDiff(oldText, newText) {
|
|
7392
|
+
const changes = [];
|
|
7393
|
+
const oldLines = oldText.split("\n");
|
|
7394
|
+
const newLines = newText.split("\n");
|
|
7395
|
+
let i = 0;
|
|
7396
|
+
let j = 0;
|
|
7397
|
+
while (i < oldLines.length || j < newLines.length) {
|
|
7398
|
+
if (i >= oldLines.length) {
|
|
7399
|
+
changes.push({
|
|
7400
|
+
type: "addition",
|
|
7401
|
+
content: newLines[j],
|
|
7402
|
+
position: j
|
|
7403
|
+
});
|
|
7404
|
+
j++;
|
|
7405
|
+
} else if (j >= newLines.length) {
|
|
7406
|
+
changes.push({
|
|
7407
|
+
type: "deletion",
|
|
7408
|
+
content: oldLines[i],
|
|
7409
|
+
position: i
|
|
7410
|
+
});
|
|
7411
|
+
i++;
|
|
7412
|
+
} else if (oldLines[i] === newLines[j]) {
|
|
7413
|
+
i++;
|
|
7414
|
+
j++;
|
|
7415
|
+
} else {
|
|
7416
|
+
const oldLineInNew = newLines.indexOf(oldLines[i], j);
|
|
7417
|
+
const newLineInOld = oldLines.indexOf(newLines[j], i);
|
|
7418
|
+
if (oldLineInNew === -1 && newLineInOld === -1) {
|
|
7419
|
+
changes.push({
|
|
7420
|
+
type: "modification",
|
|
7421
|
+
content: `${oldLines[i]} -> ${newLines[j]}`,
|
|
7422
|
+
position: i
|
|
7423
|
+
});
|
|
7424
|
+
i++;
|
|
7425
|
+
j++;
|
|
7426
|
+
} else if (oldLineInNew === -1) {
|
|
7427
|
+
changes.push({
|
|
7428
|
+
type: "deletion",
|
|
7429
|
+
content: oldLines[i],
|
|
7430
|
+
position: i
|
|
7431
|
+
});
|
|
7432
|
+
i++;
|
|
7433
|
+
} else {
|
|
7434
|
+
changes.push({
|
|
7435
|
+
type: "addition",
|
|
7436
|
+
content: newLines[j],
|
|
7437
|
+
position: j
|
|
7438
|
+
});
|
|
7439
|
+
j++;
|
|
7440
|
+
}
|
|
7441
|
+
}
|
|
7442
|
+
}
|
|
7443
|
+
return changes;
|
|
7444
|
+
}
|
|
7445
|
+
var VersionHistoryContext = createContext3(null);
|
|
7446
|
+
function VersionHistoryProvider({
|
|
7447
|
+
children,
|
|
7448
|
+
config,
|
|
7449
|
+
initialVersions = [],
|
|
7450
|
+
onVersionsChange,
|
|
7451
|
+
getCurrentContent
|
|
7452
|
+
}) {
|
|
7453
|
+
const [state, dispatch] = useReducer3(versionHistoryReducer, {
|
|
7454
|
+
...initialState3,
|
|
7455
|
+
versions: initialVersions,
|
|
7456
|
+
autoSaveEnabled: config?.autoSave ?? true,
|
|
7457
|
+
autoSaveInterval: config?.autoSaveInterval ?? 6e4
|
|
7458
|
+
});
|
|
7459
|
+
const subscribersRef = useRef9(/* @__PURE__ */ new Set());
|
|
7460
|
+
const autoSaveTimerRef = useRef9(null);
|
|
7461
|
+
const lastContentRef = useRef9("");
|
|
7462
|
+
const versionNumberRef = useRef9(initialVersions.length);
|
|
7463
|
+
useEffect11(() => {
|
|
7464
|
+
if (config?.onLoad) {
|
|
7465
|
+
config.onLoad().then((versions) => {
|
|
7466
|
+
dispatch({ type: "SET_VERSIONS", versions });
|
|
7467
|
+
versionNumberRef.current = versions.length;
|
|
7468
|
+
});
|
|
7469
|
+
}
|
|
7470
|
+
}, [config]);
|
|
7471
|
+
useEffect11(() => {
|
|
7472
|
+
onVersionsChange?.(state.versions);
|
|
7473
|
+
if (config?.onSave) {
|
|
7474
|
+
config.onSave(state.versions);
|
|
7475
|
+
}
|
|
7476
|
+
}, [state.versions, onVersionsChange, config]);
|
|
7477
|
+
useEffect11(() => {
|
|
7478
|
+
if (!state.autoSaveEnabled || !config || !getCurrentContent) return;
|
|
7479
|
+
autoSaveTimerRef.current = setInterval(() => {
|
|
7480
|
+
const { html, json } = getCurrentContent();
|
|
7481
|
+
if (html !== lastContentRef.current) {
|
|
7482
|
+
lastContentRef.current = html;
|
|
7483
|
+
createVersion(html, json, { isAutoSave: true });
|
|
7484
|
+
}
|
|
7485
|
+
}, state.autoSaveInterval);
|
|
7486
|
+
return () => {
|
|
7487
|
+
if (autoSaveTimerRef.current) {
|
|
7488
|
+
clearInterval(autoSaveTimerRef.current);
|
|
7489
|
+
}
|
|
7490
|
+
};
|
|
7491
|
+
}, [state.autoSaveEnabled, state.autoSaveInterval, config, getCurrentContent]);
|
|
7492
|
+
const emitEvent = useCallback9((event) => {
|
|
7493
|
+
subscribersRef.current.forEach((callback) => callback(event));
|
|
7494
|
+
}, []);
|
|
7495
|
+
const createVersion = useCallback9((content, jsonContent, options) => {
|
|
7496
|
+
if (!config?.currentUser) return null;
|
|
7497
|
+
const maxVersions = config.maxVersions ?? 100;
|
|
7498
|
+
let versions = state.versions;
|
|
7499
|
+
if (versions.length >= maxVersions) {
|
|
7500
|
+
const oldestUnpinned = [...versions].reverse().find((v) => !v.isPinned);
|
|
7501
|
+
if (oldestUnpinned) {
|
|
7502
|
+
versions = versions.filter((v) => v.id !== oldestUnpinned.id);
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
versionNumberRef.current++;
|
|
7506
|
+
const textContent = content.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
7507
|
+
const version = {
|
|
7508
|
+
id: generateId2(),
|
|
7509
|
+
number: versionNumberRef.current,
|
|
7510
|
+
title: options?.title,
|
|
7511
|
+
content,
|
|
7512
|
+
jsonContent,
|
|
7513
|
+
textContent,
|
|
7514
|
+
author: config.currentUser,
|
|
7515
|
+
createdAt: Date.now(),
|
|
7516
|
+
wordCount: countWords(textContent),
|
|
7517
|
+
characterCount: textContent.length,
|
|
7518
|
+
isAutoSave: options?.isAutoSave ?? false,
|
|
7519
|
+
isPinned: false
|
|
7520
|
+
};
|
|
7521
|
+
dispatch({ type: "ADD_VERSION", version });
|
|
7522
|
+
emitEvent({ type: "version-created", version });
|
|
7523
|
+
return version;
|
|
7524
|
+
}, [config, state.versions, emitEvent]);
|
|
7525
|
+
const deleteVersion = useCallback9((versionId) => {
|
|
7526
|
+
dispatch({ type: "DELETE_VERSION", versionId });
|
|
7527
|
+
emitEvent({ type: "version-deleted", versionId });
|
|
7528
|
+
}, [emitEvent]);
|
|
7529
|
+
const restoreVersion = useCallback9((versionId) => {
|
|
7530
|
+
const version = state.versions.find((v) => v.id === versionId);
|
|
7531
|
+
if (!version) return null;
|
|
7532
|
+
config?.onRestore?.(version);
|
|
7533
|
+
emitEvent({ type: "version-restored", versionId });
|
|
7534
|
+
return version.content;
|
|
7535
|
+
}, [state.versions, config, emitEvent]);
|
|
7536
|
+
const pinVersion = useCallback9((versionId) => {
|
|
7537
|
+
dispatch({ type: "PIN_VERSION", versionId });
|
|
7538
|
+
emitEvent({ type: "version-pinned", versionId });
|
|
7539
|
+
}, [emitEvent]);
|
|
7540
|
+
const unpinVersion = useCallback9((versionId) => {
|
|
7541
|
+
dispatch({ type: "UNPIN_VERSION", versionId });
|
|
7542
|
+
emitEvent({ type: "version-unpinned", versionId });
|
|
7543
|
+
}, [emitEvent]);
|
|
7544
|
+
const renameVersion = useCallback9((versionId, title) => {
|
|
7545
|
+
dispatch({ type: "RENAME_VERSION", versionId, title });
|
|
7546
|
+
emitEvent({ type: "version-renamed", versionId, title });
|
|
7547
|
+
}, [emitEvent]);
|
|
7548
|
+
const viewVersion = useCallback9((versionId) => {
|
|
7549
|
+
dispatch({ type: "SET_VIEWING_VERSION", versionId });
|
|
7550
|
+
}, []);
|
|
7551
|
+
const getVersionContent = useCallback9((versionId) => {
|
|
7552
|
+
const version = state.versions.find((v) => v.id === versionId);
|
|
7553
|
+
return version?.content ?? null;
|
|
7554
|
+
}, [state.versions]);
|
|
7555
|
+
const compareVersions = useCallback9((fromId, toId) => {
|
|
7556
|
+
const fromVersion = state.versions.find((v) => v.id === fromId);
|
|
7557
|
+
const toVersion = state.versions.find((v) => v.id === toId);
|
|
7558
|
+
if (!fromVersion || !toVersion) return null;
|
|
7559
|
+
const changes = computeDiff(fromVersion.textContent, toVersion.textContent);
|
|
7560
|
+
return {
|
|
7561
|
+
fromVersionId: fromId,
|
|
7562
|
+
toVersionId: toId,
|
|
7563
|
+
changes,
|
|
7564
|
+
stats: {
|
|
7565
|
+
additions: changes.filter((c) => c.type === "addition").length,
|
|
7566
|
+
deletions: changes.filter((c) => c.type === "deletion").length,
|
|
7567
|
+
modifications: changes.filter((c) => c.type === "modification").length
|
|
7568
|
+
}
|
|
7569
|
+
};
|
|
7570
|
+
}, [state.versions]);
|
|
7571
|
+
const startCompare = useCallback9((fromId, toId) => {
|
|
7572
|
+
dispatch({ type: "START_COMPARE", fromId, toId });
|
|
7573
|
+
}, []);
|
|
7574
|
+
const stopCompare = useCallback9(() => {
|
|
7575
|
+
dispatch({ type: "STOP_COMPARE" });
|
|
7576
|
+
}, []);
|
|
7577
|
+
const togglePanel = useCallback9((isOpen) => {
|
|
7578
|
+
dispatch({ type: "TOGGLE_PANEL", isOpen });
|
|
7579
|
+
}, []);
|
|
7580
|
+
const setAutoSave = useCallback9((enabled) => {
|
|
7581
|
+
dispatch({ type: "SET_AUTO_SAVE", enabled });
|
|
7582
|
+
emitEvent({ type: "auto-save-toggled", enabled });
|
|
7583
|
+
}, [emitEvent]);
|
|
7584
|
+
const getVersion = useCallback9((versionId) => {
|
|
7585
|
+
return state.versions.find((v) => v.id === versionId);
|
|
7586
|
+
}, [state.versions]);
|
|
7587
|
+
const getLatestVersion = useCallback9(() => {
|
|
7588
|
+
return state.versions[0];
|
|
7589
|
+
}, [state.versions]);
|
|
7590
|
+
const getPinnedVersions = useCallback9(() => {
|
|
7591
|
+
return state.versions.filter((v) => v.isPinned);
|
|
7592
|
+
}, [state.versions]);
|
|
7593
|
+
const subscribe = useCallback9((callback) => {
|
|
7594
|
+
subscribersRef.current.add(callback);
|
|
7595
|
+
return () => {
|
|
7596
|
+
subscribersRef.current.delete(callback);
|
|
7597
|
+
};
|
|
7598
|
+
}, []);
|
|
7599
|
+
const value = {
|
|
7600
|
+
state,
|
|
7601
|
+
config: config ?? null,
|
|
7602
|
+
createVersion,
|
|
7603
|
+
deleteVersion,
|
|
7604
|
+
restoreVersion,
|
|
7605
|
+
pinVersion,
|
|
7606
|
+
unpinVersion,
|
|
7607
|
+
renameVersion,
|
|
7608
|
+
viewVersion,
|
|
7609
|
+
getVersionContent,
|
|
7610
|
+
compareVersions,
|
|
7611
|
+
startCompare,
|
|
7612
|
+
stopCompare,
|
|
7613
|
+
togglePanel,
|
|
7614
|
+
setAutoSave,
|
|
7615
|
+
getVersion,
|
|
7616
|
+
getLatestVersion,
|
|
7617
|
+
getPinnedVersions,
|
|
7618
|
+
subscribe,
|
|
7619
|
+
isEnabled: !!config
|
|
7620
|
+
};
|
|
7621
|
+
return /* @__PURE__ */ jsx14(VersionHistoryContext.Provider, { value, children });
|
|
7622
|
+
}
|
|
7623
|
+
function useVersionHistory() {
|
|
7624
|
+
const context = useContext3(VersionHistoryContext);
|
|
7625
|
+
if (!context) {
|
|
7626
|
+
throw new Error("useVersionHistory must be used within a VersionHistoryProvider");
|
|
7627
|
+
}
|
|
7628
|
+
return context;
|
|
7629
|
+
}
|
|
7630
|
+
function useVersionHistoryOptional() {
|
|
7631
|
+
return useContext3(VersionHistoryContext);
|
|
7632
|
+
}
|
|
7633
|
+
|
|
7634
|
+
// src/history/VersionHistoryPanel.tsx
|
|
7635
|
+
import { useState as useState9 } from "react";
|
|
7636
|
+
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
7637
|
+
function formatDate(timestamp) {
|
|
7638
|
+
const date = new Date(timestamp);
|
|
7639
|
+
const now = /* @__PURE__ */ new Date();
|
|
7640
|
+
const isToday = date.toDateString() === now.toDateString();
|
|
7641
|
+
if (isToday) {
|
|
7642
|
+
return `Today at ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
|
|
7643
|
+
}
|
|
7644
|
+
const yesterday = new Date(now);
|
|
7645
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
7646
|
+
if (date.toDateString() === yesterday.toDateString()) {
|
|
7647
|
+
return `Yesterday at ${date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
|
|
7648
|
+
}
|
|
7649
|
+
return date.toLocaleDateString([], {
|
|
7650
|
+
month: "short",
|
|
7651
|
+
day: "numeric",
|
|
7652
|
+
hour: "2-digit",
|
|
7653
|
+
minute: "2-digit"
|
|
7654
|
+
});
|
|
7655
|
+
}
|
|
7656
|
+
function VersionItem({
|
|
7657
|
+
version,
|
|
7658
|
+
isViewing,
|
|
7659
|
+
isCompareFrom,
|
|
7660
|
+
isCompareTo,
|
|
7661
|
+
onView,
|
|
7662
|
+
onRestore,
|
|
7663
|
+
onPin,
|
|
7664
|
+
onRename,
|
|
7665
|
+
onDelete,
|
|
7666
|
+
onCompareSelect
|
|
7667
|
+
}) {
|
|
7668
|
+
const [isRenaming, setIsRenaming] = useState9(false);
|
|
7669
|
+
const [newTitle, setNewTitle] = useState9(version.title || "");
|
|
7670
|
+
const handleRename = () => {
|
|
7671
|
+
onRename(newTitle);
|
|
7672
|
+
setIsRenaming(false);
|
|
7673
|
+
};
|
|
7674
|
+
return /* @__PURE__ */ jsxs12(
|
|
7675
|
+
"div",
|
|
7676
|
+
{
|
|
7677
|
+
className: `rte-version-item ${isViewing ? "rte-version-viewing" : ""} ${isCompareFrom ? "rte-version-compare-from" : ""} ${isCompareTo ? "rte-version-compare-to" : ""}`,
|
|
7678
|
+
children: [
|
|
7679
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-header", onClick: onView, children: [
|
|
7680
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-info", children: [
|
|
7681
|
+
isRenaming ? /* @__PURE__ */ jsx15(
|
|
7682
|
+
"input",
|
|
7683
|
+
{
|
|
7684
|
+
type: "text",
|
|
7685
|
+
value: newTitle,
|
|
7686
|
+
onChange: (e) => setNewTitle(e.target.value),
|
|
7687
|
+
onBlur: handleRename,
|
|
7688
|
+
onKeyDown: (e) => e.key === "Enter" && handleRename(),
|
|
7689
|
+
className: "rte-version-rename-input",
|
|
7690
|
+
onClick: (e) => e.stopPropagation(),
|
|
7691
|
+
autoFocus: true
|
|
7692
|
+
}
|
|
7693
|
+
) : /* @__PURE__ */ jsxs12("span", { className: "rte-version-title", children: [
|
|
7694
|
+
version.title || `Version ${version.number}`,
|
|
7695
|
+
version.isPinned && /* @__PURE__ */ jsx15("span", { className: "rte-version-pin-icon", children: "\u{1F4CC}" }),
|
|
7696
|
+
version.isAutoSave && /* @__PURE__ */ jsx15("span", { className: "rte-version-auto-badge", children: "Auto" })
|
|
7697
|
+
] }),
|
|
7698
|
+
/* @__PURE__ */ jsx15("span", { className: "rte-version-time", children: formatDate(version.createdAt) })
|
|
7699
|
+
] }),
|
|
7700
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-author", children: [
|
|
7701
|
+
version.author.avatar ? /* @__PURE__ */ jsx15("img", { src: version.author.avatar, alt: version.author.name, className: "rte-version-avatar" }) : /* @__PURE__ */ jsx15("div", { className: "rte-version-avatar-placeholder", children: version.author.name.charAt(0).toUpperCase() }),
|
|
7702
|
+
/* @__PURE__ */ jsx15("span", { children: version.author.name })
|
|
7703
|
+
] })
|
|
7704
|
+
] }),
|
|
7705
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-stats", children: [
|
|
7706
|
+
/* @__PURE__ */ jsxs12("span", { children: [
|
|
7707
|
+
version.wordCount,
|
|
7708
|
+
" words"
|
|
7709
|
+
] }),
|
|
7710
|
+
/* @__PURE__ */ jsxs12("span", { children: [
|
|
7711
|
+
version.characterCount,
|
|
7712
|
+
" chars"
|
|
7713
|
+
] })
|
|
7714
|
+
] }),
|
|
7715
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-actions", children: [
|
|
7716
|
+
/* @__PURE__ */ jsx15("button", { onClick: onRestore, className: "rte-version-btn", title: "Restore this version", children: "\u21BA Restore" }),
|
|
7717
|
+
/* @__PURE__ */ jsx15("button", { onClick: onCompareSelect, className: "rte-version-btn", title: "Compare with another version", children: "\u21C4 Compare" }),
|
|
7718
|
+
/* @__PURE__ */ jsx15("button", { onClick: onPin, className: "rte-version-btn", title: version.isPinned ? "Unpin" : "Pin", children: version.isPinned ? "\u{1F4CC} Unpin" : "\u{1F4CC} Pin" }),
|
|
7719
|
+
/* @__PURE__ */ jsx15(
|
|
7720
|
+
"button",
|
|
7721
|
+
{
|
|
7722
|
+
onClick: () => setIsRenaming(true),
|
|
7723
|
+
className: "rte-version-btn",
|
|
7724
|
+
title: "Rename version",
|
|
7725
|
+
children: "\u270F\uFE0F"
|
|
7726
|
+
}
|
|
7727
|
+
),
|
|
7728
|
+
!version.isPinned && /* @__PURE__ */ jsx15("button", { onClick: onDelete, className: "rte-version-btn rte-version-btn-delete", title: "Delete", children: "\u{1F5D1}\uFE0F" })
|
|
7729
|
+
] })
|
|
7730
|
+
]
|
|
7731
|
+
}
|
|
7732
|
+
);
|
|
7733
|
+
}
|
|
7734
|
+
function ComparisonView({
|
|
7735
|
+
fromVersion,
|
|
7736
|
+
toVersion,
|
|
7737
|
+
onClose
|
|
7738
|
+
}) {
|
|
7739
|
+
const versionHistory = useVersionHistoryOptional();
|
|
7740
|
+
if (!versionHistory) return null;
|
|
7741
|
+
const comparison = versionHistory.compareVersions(fromVersion.id, toVersion.id);
|
|
7742
|
+
return /* @__PURE__ */ jsxs12("div", { className: "rte-version-comparison", children: [
|
|
7743
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-comparison-header", children: [
|
|
7744
|
+
/* @__PURE__ */ jsx15("h4", { children: "Comparing Versions" }),
|
|
7745
|
+
/* @__PURE__ */ jsx15("button", { onClick: onClose, className: "rte-version-comparison-close", children: "\xD7" })
|
|
7746
|
+
] }),
|
|
7747
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-comparison-info", children: [
|
|
7748
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-comparison-from", children: [
|
|
7749
|
+
/* @__PURE__ */ jsx15("span", { className: "rte-version-comparison-label", children: "From:" }),
|
|
7750
|
+
fromVersion.title || `Version ${fromVersion.number}`
|
|
7751
|
+
] }),
|
|
7752
|
+
/* @__PURE__ */ jsx15("span", { className: "rte-version-comparison-arrow", children: "\u2192" }),
|
|
7753
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-comparison-to", children: [
|
|
7754
|
+
/* @__PURE__ */ jsx15("span", { className: "rte-version-comparison-label", children: "To:" }),
|
|
7755
|
+
toVersion.title || `Version ${toVersion.number}`
|
|
7756
|
+
] })
|
|
7757
|
+
] }),
|
|
7758
|
+
comparison && /* @__PURE__ */ jsxs12("div", { className: "rte-version-comparison-stats", children: [
|
|
7759
|
+
/* @__PURE__ */ jsxs12("span", { className: "rte-comparison-stat rte-comparison-additions", children: [
|
|
7760
|
+
"+",
|
|
7761
|
+
comparison.stats.additions,
|
|
7762
|
+
" additions"
|
|
7763
|
+
] }),
|
|
7764
|
+
/* @__PURE__ */ jsxs12("span", { className: "rte-comparison-stat rte-comparison-deletions", children: [
|
|
7765
|
+
"-",
|
|
7766
|
+
comparison.stats.deletions,
|
|
7767
|
+
" deletions"
|
|
7768
|
+
] }),
|
|
7769
|
+
/* @__PURE__ */ jsxs12("span", { className: "rte-comparison-stat rte-comparison-modifications", children: [
|
|
7770
|
+
"~",
|
|
7771
|
+
comparison.stats.modifications,
|
|
7772
|
+
" modifications"
|
|
7773
|
+
] })
|
|
7774
|
+
] }),
|
|
7775
|
+
/* @__PURE__ */ jsx15("div", { className: "rte-version-comparison-diff", children: comparison?.changes.map((change, index) => /* @__PURE__ */ jsxs12("div", { className: `rte-diff-line rte-diff-${change.type}`, children: [
|
|
7776
|
+
/* @__PURE__ */ jsx15("span", { className: "rte-diff-indicator", children: change.type === "addition" ? "+" : change.type === "deletion" ? "-" : "~" }),
|
|
7777
|
+
/* @__PURE__ */ jsx15("span", { className: "rte-diff-content", children: change.content })
|
|
7778
|
+
] }, index)) })
|
|
7779
|
+
] });
|
|
7780
|
+
}
|
|
7781
|
+
function VersionHistoryPanel({ position = "right", className = "" }) {
|
|
7782
|
+
const versionHistory = useVersionHistoryOptional();
|
|
7783
|
+
const [compareFromId, setCompareFromId] = useState9(null);
|
|
7784
|
+
if (!versionHistory?.isEnabled || !versionHistory.state.isPanelOpen) {
|
|
7785
|
+
return null;
|
|
7786
|
+
}
|
|
7787
|
+
const {
|
|
7788
|
+
state,
|
|
7789
|
+
togglePanel,
|
|
7790
|
+
viewVersion,
|
|
7791
|
+
restoreVersion,
|
|
7792
|
+
pinVersion,
|
|
7793
|
+
unpinVersion,
|
|
7794
|
+
renameVersion,
|
|
7795
|
+
deleteVersion,
|
|
7796
|
+
setAutoSave,
|
|
7797
|
+
getVersion,
|
|
7798
|
+
createVersion,
|
|
7799
|
+
stopCompare
|
|
7800
|
+
} = versionHistory;
|
|
7801
|
+
const handleRestore = (versionId) => {
|
|
7802
|
+
const content = restoreVersion(versionId);
|
|
7803
|
+
if (content) {
|
|
7804
|
+
viewVersion(null);
|
|
7805
|
+
}
|
|
7806
|
+
};
|
|
7807
|
+
const handleCompareSelect = (versionId) => {
|
|
7808
|
+
if (!compareFromId) {
|
|
7809
|
+
setCompareFromId(versionId);
|
|
7810
|
+
} else {
|
|
7811
|
+
versionHistory.startCompare(compareFromId, versionId);
|
|
7812
|
+
setCompareFromId(null);
|
|
7813
|
+
}
|
|
7814
|
+
};
|
|
7815
|
+
const handleCancelCompare = () => {
|
|
7816
|
+
setCompareFromId(null);
|
|
7817
|
+
stopCompare();
|
|
7818
|
+
};
|
|
7819
|
+
const compareFromVersion = state.compareFromId ? getVersion(state.compareFromId) : void 0;
|
|
7820
|
+
const compareToVersion = state.compareToId ? getVersion(state.compareToId) : void 0;
|
|
7821
|
+
return /* @__PURE__ */ jsxs12("div", { className: `rte-version-panel rte-version-panel-${position} ${className}`, children: [
|
|
7822
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-panel-header", children: [
|
|
7823
|
+
/* @__PURE__ */ jsx15("h3", { children: "Version History" }),
|
|
7824
|
+
/* @__PURE__ */ jsx15("button", { onClick: () => togglePanel(false), className: "rte-version-close-btn", children: "\xD7" })
|
|
7825
|
+
] }),
|
|
7826
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-autosave", children: [
|
|
7827
|
+
/* @__PURE__ */ jsxs12("label", { className: "rte-version-autosave-label", children: [
|
|
7828
|
+
/* @__PURE__ */ jsx15(
|
|
7829
|
+
"input",
|
|
7830
|
+
{
|
|
7831
|
+
type: "checkbox",
|
|
7832
|
+
checked: state.autoSaveEnabled,
|
|
7833
|
+
onChange: (e) => setAutoSave(e.target.checked)
|
|
7834
|
+
}
|
|
7835
|
+
),
|
|
7836
|
+
"Auto-save versions"
|
|
7837
|
+
] }),
|
|
7838
|
+
/* @__PURE__ */ jsx15(
|
|
7839
|
+
"button",
|
|
7840
|
+
{
|
|
7841
|
+
onClick: () => createVersion("", void 0, { isAutoSave: false }),
|
|
7842
|
+
className: "rte-version-save-btn",
|
|
7843
|
+
children: "Save Now"
|
|
7844
|
+
}
|
|
7845
|
+
)
|
|
7846
|
+
] }),
|
|
7847
|
+
compareFromId && !state.isComparing && /* @__PURE__ */ jsxs12("div", { className: "rte-version-compare-mode", children: [
|
|
7848
|
+
/* @__PURE__ */ jsx15("span", { children: "Select another version to compare" }),
|
|
7849
|
+
/* @__PURE__ */ jsx15("button", { onClick: handleCancelCompare, children: "Cancel" })
|
|
7850
|
+
] }),
|
|
7851
|
+
state.isComparing && compareFromVersion && compareToVersion && /* @__PURE__ */ jsx15(
|
|
7852
|
+
ComparisonView,
|
|
7853
|
+
{
|
|
7854
|
+
fromVersion: compareFromVersion,
|
|
7855
|
+
toVersion: compareToVersion,
|
|
7856
|
+
onClose: handleCancelCompare
|
|
7857
|
+
}
|
|
7858
|
+
),
|
|
7859
|
+
!state.isComparing && /* @__PURE__ */ jsx15("div", { className: "rte-version-list", children: state.versions.length === 0 ? /* @__PURE__ */ jsx15("div", { className: "rte-version-empty", children: "No versions saved yet. Changes will be auto-saved periodically." }) : /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
7860
|
+
state.versions.some((v) => v.isPinned) && /* @__PURE__ */ jsxs12("div", { className: "rte-version-section", children: [
|
|
7861
|
+
/* @__PURE__ */ jsx15("div", { className: "rte-version-section-title", children: "\u{1F4CC} Pinned" }),
|
|
7862
|
+
state.versions.filter((v) => v.isPinned).map((version) => /* @__PURE__ */ jsx15(
|
|
7863
|
+
VersionItem,
|
|
7864
|
+
{
|
|
7865
|
+
version,
|
|
7866
|
+
isViewing: state.viewingVersionId === version.id,
|
|
7867
|
+
isCompareFrom: compareFromId === version.id,
|
|
7868
|
+
isCompareTo: false,
|
|
7869
|
+
onView: () => viewVersion(version.id),
|
|
7870
|
+
onRestore: () => handleRestore(version.id),
|
|
7871
|
+
onPin: () => unpinVersion(version.id),
|
|
7872
|
+
onRename: (title) => renameVersion(version.id, title),
|
|
7873
|
+
onDelete: () => deleteVersion(version.id),
|
|
7874
|
+
onCompareSelect: () => handleCompareSelect(version.id)
|
|
7875
|
+
},
|
|
7876
|
+
version.id
|
|
7877
|
+
))
|
|
7878
|
+
] }),
|
|
7879
|
+
/* @__PURE__ */ jsxs12("div", { className: "rte-version-section", children: [
|
|
7880
|
+
/* @__PURE__ */ jsx15("div", { className: "rte-version-section-title", children: "Recent" }),
|
|
7881
|
+
state.versions.filter((v) => !v.isPinned).map((version) => /* @__PURE__ */ jsx15(
|
|
7882
|
+
VersionItem,
|
|
7883
|
+
{
|
|
7884
|
+
version,
|
|
7885
|
+
isViewing: state.viewingVersionId === version.id,
|
|
7886
|
+
isCompareFrom: compareFromId === version.id,
|
|
7887
|
+
isCompareTo: false,
|
|
7888
|
+
onView: () => viewVersion(version.id),
|
|
7889
|
+
onRestore: () => handleRestore(version.id),
|
|
7890
|
+
onPin: () => pinVersion(version.id),
|
|
7891
|
+
onRename: (title) => renameVersion(version.id, title),
|
|
7892
|
+
onDelete: () => deleteVersion(version.id),
|
|
7893
|
+
onCompareSelect: () => handleCompareSelect(version.id)
|
|
7894
|
+
},
|
|
7895
|
+
version.id
|
|
7896
|
+
))
|
|
7897
|
+
] })
|
|
7898
|
+
] }) }),
|
|
7899
|
+
state.viewingVersionId && /* @__PURE__ */ jsxs12("div", { className: "rte-version-viewing-indicator", children: [
|
|
7900
|
+
/* @__PURE__ */ jsxs12("span", { children: [
|
|
7901
|
+
"Viewing: ",
|
|
7902
|
+
getVersion(state.viewingVersionId)?.title || `Version ${getVersion(state.viewingVersionId)?.number}`
|
|
7903
|
+
] }),
|
|
7904
|
+
/* @__PURE__ */ jsx15("button", { onClick: () => viewVersion(null), className: "rte-version-back-btn", children: "\u2190 Back to current" })
|
|
7905
|
+
] })
|
|
7906
|
+
] });
|
|
7907
|
+
}
|
|
8677
7908
|
export {
|
|
7909
|
+
CollaborationProvider,
|
|
7910
|
+
CommentsPanel,
|
|
7911
|
+
CommentsProvider,
|
|
8678
7912
|
DEFAULT_FEATURES,
|
|
7913
|
+
DEFAULT_REACTION_EMOJIS,
|
|
8679
7914
|
EMOJI_CATEGORIES,
|
|
8680
7915
|
EditorRegistry,
|
|
8681
7916
|
Emoji,
|
|
@@ -8683,12 +7918,15 @@ export {
|
|
|
8683
7918
|
Fullscreen,
|
|
8684
7919
|
Indent,
|
|
8685
7920
|
LineHeight,
|
|
7921
|
+
PresenceIndicator,
|
|
8686
7922
|
Print,
|
|
8687
7923
|
RichTextEditor,
|
|
8688
7924
|
TipTapAdapter,
|
|
8689
7925
|
TipTapEditorComponent,
|
|
8690
7926
|
TipTapToolbar,
|
|
8691
7927
|
UnifiedEditor,
|
|
7928
|
+
VersionHistoryPanel,
|
|
7929
|
+
VersionHistoryProvider,
|
|
8692
7930
|
Video,
|
|
8693
7931
|
blogToolbar,
|
|
8694
7932
|
codeToolbar,
|
|
@@ -8710,6 +7948,12 @@ export {
|
|
|
8710
7948
|
registerAdapter,
|
|
8711
7949
|
simpleToolbar,
|
|
8712
7950
|
toolbarPresets,
|
|
8713
|
-
unregisterAdapter
|
|
7951
|
+
unregisterAdapter,
|
|
7952
|
+
useCollaboration,
|
|
7953
|
+
useCollaborationOptional,
|
|
7954
|
+
useComments,
|
|
7955
|
+
useCommentsOptional,
|
|
7956
|
+
useVersionHistory,
|
|
7957
|
+
useVersionHistoryOptional
|
|
8714
7958
|
};
|
|
8715
7959
|
//# sourceMappingURL=index.mjs.map
|