From cd52acc415a9332ba210817f1ab6e0647591b0a4 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 31 Jan 2026 19:01:43 +0000 Subject: [PATCH] WIP 2 --- .../components/history-diff.module.css | 14 +-- .../components/history-editor.tsx | 117 +++++++----------- 2 files changed, 51 insertions(+), 80 deletions(-) diff --git a/apps/client/src/features/page-history/components/history-diff.module.css b/apps/client/src/features/page-history/components/history-diff.module.css index ebd6105e..b79b0a60 100644 --- a/apps/client/src/features/page-history/components/history-diff.module.css +++ b/apps/client/src/features/page-history/components/history-diff.module.css @@ -3,7 +3,10 @@ light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); border-radius: rem(10px); padding: rem(12px); - background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); + background: light-dark( + var(--mantine-color-gray-0), + var(--mantine-color-dark-7) + ); } :global(.history-diff-added) { @@ -16,12 +19,9 @@ position: absolute; z-index: -1; inset: 0; - left: rem(-12px); right: 0; - border-left: rem(4px) solid - light-dark(var(--mantine-color-green-6), var(--mantine-color-green-4)); background: light-dark(var(--mantine-color-green-0), rgba(0, 255, 0, 0.06)); - border-radius: rem(6px); + border-radius: rem(4px); pointer-events: none; } @@ -39,8 +39,6 @@ inset: 0; left: rem(-12px); right: 0; - border-left: rem(4px) solid - light-dark(var(--mantine-color-red-6), var(--mantine-color-red-4)); border-top: rem(1px) dashed light-dark(var(--mantine-color-red-4), var(--mantine-color-red-6)); border-right: rem(1px) dashed @@ -51,5 +49,3 @@ border-radius: rem(6px); pointer-events: none; } - - diff --git a/apps/client/src/features/page-history/components/history-editor.tsx b/apps/client/src/features/page-history/components/history-editor.tsx index bdd43722..022a8b2f 100644 --- a/apps/client/src/features/page-history/components/history-editor.tsx +++ b/apps/client/src/features/page-history/components/history-editor.tsx @@ -1,14 +1,14 @@ import "@/features/editor/styles/index.css"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { EditorContent, useEditor } from "@tiptap/react"; import { mainExtensions } from "@/features/editor/extensions/extensions"; import { Badge, Divider, Group, Text, Title } from "@mantine/core"; import { Decoration, DecorationSet } from "@tiptap/pm/view"; -import { computeHistoryBlockDiff } from "@/features/page-history/utils/history-diff"; import classes from "./history-diff.module.css"; import historyClasses from "./history.module.css"; import { recreateTransform } from "@docmost/editor-ext"; -import { Node, Schema, DOMSerializer } from "@tiptap/pm/model"; +import { Node } from "@tiptap/pm/model"; +import { ChangeSet, simplifyChanges } from "prosemirror-changeset"; export interface HistoryEditorProps { title: string; @@ -35,90 +35,65 @@ export function HistoryEditor({ }); useEffect(() => { - if (editor && previousContent && content) { - const schema = editor.schema; + if (!editor || !content) return; + let decorationSet = DecorationSet.empty; + let addedCount = 0; + let deletedCount = 0; + + if (previousContent) { try { - console.log( - "previousContent type:", - previousContent?.type, - "content type:", - content?.type, - ); + const schema = editor.schema; const docOld = Node.fromJSON(schema, previousContent); const docNew = Node.fromJSON(schema, content); - const t0 = performance.now(); - const transform = recreateTransform(docOld, docNew, { + const tr = recreateTransform(docOld, docNew, { complexSteps: true, wordDiffs: true, simplifyDiff: true, }); - console.log( - `recreateTransform: ${(performance.now() - t0).toFixed(3)}ms`, + + const changeSet = ChangeSet.create(docOld).addSteps( + tr.doc, + tr.mapping.maps, + [], ); + const changes = simplifyChanges(changeSet.changes, docNew); - //console.log(transform); - } catch (e) { - console.error("Node.fromJSON failed:", e); - } - } - }, [editor]); + editor.commands.setContent(content); - useEffect(() => { - if (editor && content) { - let decorationSet = DecorationSet.empty; - let addedCount = 0; - let deletedCount = 0; - - if (previousContent) { - try { - const currentDoc = editor.schema.nodeFromJSON(content); - const prevDoc = editor.schema.nodeFromJSON(previousContent); - const { - diffDoc, - addedNodeRanges, - deletedNodeRanges, - addedCount: aCount, - deletedCount: dCount, - } = computeHistoryBlockDiff(currentDoc, prevDoc); - - editor.commands.setContent(diffDoc.toJSON()); - - addedCount = aCount; - deletedCount = dCount; - - const decos = addedNodeRanges.map((r) => - Decoration.node(r.from, r.to, { class: "history-diff-added" }), - ); - const deletedDecos = deletedNodeRanges.map((r) => - Decoration.node(r.from, r.to, { class: "history-diff-deleted" }), - ); - - decorationSet = DecorationSet.create(diffDoc, [ - ...decos, - ...deletedDecos, - ]); - } catch { - decorationSet = DecorationSet.empty; - addedCount = 0; - deletedCount = 0; - editor.commands.setContent(content); + const decorations: Decoration[] = []; + for (const change of changes) { + if (change.toB > change.fromB) { + decorations.push( + Decoration.inline(change.fromB, change.toB, { + class: "history-diff-added", + }), + ); + addedCount += 1; + } + if (change.toA > change.fromA) { + deletedCount += 1; + } } - } else { + + decorationSet = DecorationSet.create(docNew, decorations); + } catch (e) { + console.error("History diff failed:", e); editor.commands.setContent(content); } - - setDiffCounts({ added: addedCount, deleted: deletedCount }); - - const existingEditorProps = editor.options.editorProps ?? {}; - editor.setOptions({ - editorProps: { - ...existingEditorProps, - decorations: () => decorationSet, - }, - }); + } else { + editor.commands.setContent(content); } + + setDiffCounts({ added: addedCount, deleted: deletedCount }); + + editor.setOptions({ + editorProps: { + ...editor.options.editorProps, + decorations: () => decorationSet, + }, + }); }, [title, content, editor, previousContent]); return (