This commit is contained in:
Philipinho
2026-01-17 02:42:06 +00:00
parent c3a9a52b7f
commit d9ebeb2b85
2 changed files with 44 additions and 52 deletions
@@ -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,