mirror of
https://github.com/docmost/docmost.git
synced 2026-05-18 23:44:24 +08:00
working
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
import { StarterKit } from '@tiptap/starter-kit';
|
import { StarterKit } from '@tiptap/starter-kit';
|
||||||
import { EditorState, TextSelection } from '@tiptap/pm/state';
|
|
||||||
import {
|
import {
|
||||||
initProseMirrorDoc,
|
initProseMirrorDoc,
|
||||||
relativePositionToAbsolutePosition,
|
relativePositionToAbsolutePosition,
|
||||||
updateYFragment,
|
|
||||||
} from 'y-prosemirror';
|
} from 'y-prosemirror';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
import { Document } from '@hocuspocus/server';
|
import { Document } from '@hocuspocus/server';
|
||||||
@@ -138,14 +136,12 @@ export function setYjsMark(
|
|||||||
markAttributes: Record<string, any>,
|
markAttributes: Record<string, any>,
|
||||||
) {
|
) {
|
||||||
const schema = getSchema(tiptapExtensions);
|
const schema = getSchema(tiptapExtensions);
|
||||||
const { doc: pNode, mapping } = initProseMirrorDoc(fragment, schema);
|
const { mapping } = initProseMirrorDoc(fragment, schema);
|
||||||
|
|
||||||
// Convert JSON positions to Y.js RelativePosition objects
|
// Convert JSON positions to Y.js RelativePosition objects
|
||||||
const anchorRelPos = Y.createRelativePositionFromJSON(yjsSelection.anchor);
|
const anchorRelPos = Y.createRelativePositionFromJSON(yjsSelection.anchor);
|
||||||
const headRelPos = Y.createRelativePositionFromJSON(yjsSelection.head);
|
const headRelPos = Y.createRelativePositionFromJSON(yjsSelection.head);
|
||||||
|
|
||||||
console.log(anchorRelPos, headRelPos);
|
|
||||||
|
|
||||||
const anchor = relativePositionToAbsolutePosition(
|
const anchor = relativePositionToAbsolutePosition(
|
||||||
doc,
|
doc,
|
||||||
fragment,
|
fragment,
|
||||||
@@ -159,61 +155,57 @@ export function setYjsMark(
|
|||||||
mapping,
|
mapping,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('second')
|
|
||||||
console.log(anchor, head);
|
|
||||||
|
|
||||||
if (anchor === null || head === null) {
|
if (anchor === null || head === null) {
|
||||||
throw new Error('Could not resolve Y.js relative positions to absolute positions');
|
throw new Error(
|
||||||
|
'Could not resolve Y.js relative positions to absolute positions',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = EditorState.create({
|
const from = Math.min(anchor, head);
|
||||||
doc: pNode,
|
const to = Math.max(anchor, head);
|
||||||
schema: schema,
|
|
||||||
selection: TextSelection.create(pNode, anchor, head),
|
|
||||||
});
|
|
||||||
|
|
||||||
const tr = setMarkInProsemirror(schema.marks[markName], markAttributes, state);
|
// Apply mark directly to Y.js XmlText nodes
|
||||||
|
// This bypasses updateYFragment which has compatibility issues
|
||||||
// Update the Y.js fragment with the modified ProseMirror document
|
applyMarkToYFragment(fragment, from, to, markName, markAttributes);
|
||||||
// @ts-ignore
|
|
||||||
updateYFragment(doc, fragment, tr.doc, mapping);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMarkInProsemirror(
|
function applyMarkToYFragment(
|
||||||
type: any,
|
fragment: Y.XmlFragment,
|
||||||
attributes: Record<string, any>,
|
from: number,
|
||||||
state: EditorState,
|
to: number,
|
||||||
|
markName: string,
|
||||||
|
markAttributes: Record<string, any>,
|
||||||
) {
|
) {
|
||||||
let tr = state.tr;
|
let pos = 0;
|
||||||
const { selection } = state;
|
|
||||||
const { ranges } = selection;
|
|
||||||
|
|
||||||
ranges.forEach((range) => {
|
const processItem = (item: any): boolean => {
|
||||||
const from = range.$from.pos;
|
if (pos >= to) return false;
|
||||||
const to = range.$to.pos;
|
|
||||||
|
|
||||||
state.doc.nodesBetween(from, to, (node, pos) => {
|
if (item instanceof Y.XmlText) {
|
||||||
const trimmedFrom = Math.max(pos, from);
|
const textLength = item.length;
|
||||||
const trimmedTo = Math.min(pos + node.nodeSize, to);
|
const itemEnd = pos + textLength;
|
||||||
const someHasMark = node.marks.find((mark) => mark.type === type);
|
|
||||||
|
|
||||||
if (someHasMark) {
|
if (itemEnd > from && pos < to) {
|
||||||
node.marks.forEach((mark) => {
|
const formatFrom = Math.max(0, from - pos);
|
||||||
if (type === mark.type) {
|
const formatTo = Math.min(textLength, to - pos);
|
||||||
tr = tr.addMark(
|
const formatLength = formatTo - formatFrom;
|
||||||
trimmedFrom,
|
|
||||||
trimmedTo,
|
if (formatLength > 0) {
|
||||||
type.create({
|
item.format(formatFrom, formatLength, { [markName]: markAttributes });
|
||||||
...mark.attrs,
|
}
|
||||||
...attributes,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
tr = tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
|
|
||||||
}
|
}
|
||||||
});
|
pos = itemEnd;
|
||||||
});
|
} else if (item instanceof Y.XmlElement) {
|
||||||
return tr;
|
pos++; // Opening tag
|
||||||
|
for (let i = 0; i < item.length; i++) {
|
||||||
|
if (!processItem(item.get(i))) return false;
|
||||||
|
}
|
||||||
|
pos++; // Closing tag
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < fragment.length; i++) {
|
||||||
|
if (!processItem(fragment.get(i))) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export class PersistenceExtension implements Extension {
|
|||||||
content: tiptapJson,
|
content: tiptapJson,
|
||||||
textContent: textContent,
|
textContent: textContent,
|
||||||
ydoc: ydocState,
|
ydoc: ydocState,
|
||||||
lastUpdatedById: context.user.id,
|
lastUpdatedById: context?.user?.id,
|
||||||
contributorIds: contributorIds,
|
contributorIds: contributorIds,
|
||||||
},
|
},
|
||||||
pageId,
|
pageId,
|
||||||
|
|||||||
Reference in New Issue
Block a user