chatbot-template/components/diffview.tsx

101 lines
2.8 KiB
TypeScript
Raw Normal View History

import OrderedMap from 'orderedmap';
2024-10-30 16:01:24 +05:30
import {
Schema,
type Node as ProsemirrorNode,
type MarkSpec,
DOMParser,
} from 'prosemirror-model';
import { schema } from 'prosemirror-schema-basic';
import { addListNodes } from 'prosemirror-schema-list';
import { EditorState } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import React, { useEffect, useRef } from 'react';
import { renderToString } from 'react-dom/server';
import { Streamdown } from 'streamdown';
2024-10-30 16:01:24 +05:30
import { diffEditor, DiffType } from '@/lib/editor/diff';
2024-10-30 16:01:24 +05:30
const diffSchema = new Schema({
nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
2024-10-30 16:01:24 +05:30
marks: OrderedMap.from({
...schema.spec.marks.toObject(),
diffMark: {
attrs: { type: { default: '' } },
2024-10-30 16:01:24 +05:30
toDOM(mark) {
let className = '';
2024-10-30 16:01:24 +05:30
switch (mark.attrs.type) {
case DiffType.Inserted:
className =
'bg-green-100 text-green-700 dark:bg-green-500/70 dark:text-green-300';
2024-10-30 16:01:24 +05:30
break;
case DiffType.Deleted:
className =
'bg-red-100 line-through text-red-600 dark:bg-red-500/70 dark:text-red-300';
2024-10-30 16:01:24 +05:30
break;
default:
className = '';
2024-10-30 16:01:24 +05:30
}
return ['span', { class: className }, 0];
2024-10-30 16:01:24 +05:30
},
} as MarkSpec,
}),
});
function computeDiff(oldDoc: ProsemirrorNode, newDoc: ProsemirrorNode) {
return diffEditor(diffSchema, oldDoc.toJSON(), newDoc.toJSON());
}
type DiffEditorProps = {
oldContent: string;
newContent: string;
};
export const DiffView = ({ oldContent, newContent }: DiffEditorProps) => {
const editorRef = useRef<HTMLDivElement>(null);
const viewRef = useRef<EditorView | null>(null);
useEffect(() => {
if (editorRef.current && !viewRef.current) {
const parser = DOMParser.fromSchema(diffSchema);
const oldHtmlContent = renderToString(
<Streamdown>{oldContent}</Streamdown>,
2024-10-30 16:01:24 +05:30
);
const newHtmlContent = renderToString(
<Streamdown>{newContent}</Streamdown>,
2024-10-30 16:01:24 +05:30
);
const oldContainer = document.createElement('div');
2024-10-30 16:01:24 +05:30
oldContainer.innerHTML = oldHtmlContent;
const newContainer = document.createElement('div');
2024-10-30 16:01:24 +05:30
newContainer.innerHTML = newHtmlContent;
const oldDoc = parser.parse(oldContainer);
const newDoc = parser.parse(newContainer);
const diffedDoc = computeDiff(oldDoc, newDoc);
const state = EditorState.create({
doc: diffedDoc,
plugins: [],
});
viewRef.current = new EditorView(editorRef.current, {
state,
editable: () => false,
});
}
return () => {
if (viewRef.current) {
viewRef.current.destroy();
viewRef.current = null;
}
};
}, [oldContent, newContent]);
return <div className="diff-editor" ref={editorRef} />;
};