From 7dcbd5e9d9f2c4e9d5fd3b68dcf5659e6c2896cd Mon Sep 17 00:00:00 2001
From: thiloho <123883702+thiloho@users.noreply.github.com>
Date: Thu, 10 Apr 2025 20:54:58 +0200
Subject: [PATCH] Use js-diff instead of diff-match-patch for word level diffs
---
nix/package.nix | 2 +-
web-app/package-lock.json | 25 +++++++++--------
web-app/package.json | 4 +--
.../website/[websiteId]/logs/+page.server.ts | 28 ++++++++++---------
.../website/[websiteId]/logs/+page.svelte | 4 +--
5 files changed, 33 insertions(+), 30 deletions(-)
diff --git a/nix/package.nix b/nix/package.nix
index 8f48ad7..a1e45d1 100644
--- a/nix/package.nix
+++ b/nix/package.nix
@@ -10,7 +10,7 @@ let
web = buildNpmPackage {
name = "web-app";
src = ../web-app;
- npmDepsHash = "sha256-J58LwSEQa0p6J6h/wPhpGY/60n9a7TOV5WfNm4K1NH0=";
+ npmDepsHash = "sha256-ab7MJ5vl6XNaAHTyzRxj/Zpk1nEKQLzGmPGJdDrdemg=";
npmFlags = [ "--legacy-peer-deps" ];
installPhase = ''
mkdir -p $out/web-app
diff --git a/web-app/package-lock.json b/web-app/package-lock.json
index 9a58ec8..3231f8f 100644
--- a/web-app/package-lock.json
+++ b/web-app/package-lock.json
@@ -8,7 +8,7 @@
"name": "web-app",
"version": "0.0.1",
"dependencies": {
- "diff-match-patch": "1.0.5",
+ "diff": "7.0.0",
"highlight.js": "11.11.1",
"isomorphic-dompurify": "2.22.0",
"marked": "15.0.7",
@@ -20,7 +20,7 @@
"@sveltejs/adapter-node": "5.2.12",
"@sveltejs/kit": "2.20.2",
"@sveltejs/vite-plugin-svelte": "5.0.3",
- "@types/diff-match-patch": "1.0.36",
+ "@types/diff": "7.0.2",
"@types/eslint": "9.6.1",
"@types/eslint__js": "9.14.0",
"@types/eslint-config-prettier": "6.11.3",
@@ -1425,10 +1425,10 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/diff-match-patch": {
- "version": "1.0.36",
- "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz",
- "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==",
+ "node_modules/@types/diff": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz",
+ "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==",
"dev": true,
"license": "MIT"
},
@@ -2125,11 +2125,14 @@
"dev": true,
"license": "MIT"
},
- "node_modules/diff-match-patch": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
- "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
- "license": "Apache-2.0"
+ "node_modules/diff": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
+ "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
},
"node_modules/dompurify": {
"version": "3.2.4",
diff --git a/web-app/package.json b/web-app/package.json
index 2b7d48b..200c167 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -19,7 +19,7 @@
"@sveltejs/adapter-node": "5.2.12",
"@sveltejs/kit": "2.20.2",
"@sveltejs/vite-plugin-svelte": "5.0.3",
- "@types/diff-match-patch": "1.0.36",
+ "@types/diff": "7.0.2",
"@types/eslint": "9.6.1",
"@types/eslint__js": "9.14.0",
"@types/eslint-config-prettier": "6.11.3",
@@ -38,7 +38,7 @@
"vite": "6.2.5"
},
"dependencies": {
- "diff-match-patch": "1.0.5",
+ "diff": "7.0.0",
"highlight.js": "11.11.1",
"isomorphic-dompurify": "2.22.0",
"marked": "15.0.7",
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.server.ts b/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.server.ts
index e9ac293..e3cec82 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.server.ts
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.server.ts
@@ -3,6 +3,7 @@ import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import type { ChangeLog, User, Collab } from "$lib/db-schema";
import DiffMatchPatch from "diff-match-patch";
import { PAGINATION_MAX_ITEMS } from "$lib/utils";
+import * as Diff from "diff";
export const load: PageServerLoad = async ({ parent, fetch, params, url }) => {
const userFilter = url.searchParams.get("user");
@@ -76,22 +77,19 @@ export const actions: Actions = {
computeDiff: async ({ request, fetch }) => {
const data = await request.formData();
- const dmp = new DiffMatchPatch();
-
const htmlDiff = (oldValue: string, newValue: string) => {
- const diff = dmp.diff_main(oldValue, newValue);
+ const diff = Diff.diffWordsWithSpace(oldValue, newValue);
return diff
- .map(([op, text]) => {
- const escapedText = text.replace(//g, ">");
+ .map((part) => {
+ const escapedText = part.value.replace(//g, ">");
- switch (op) {
- case 1:
- return `${escapedText}`;
- case -1:
- return `${escapedText}`;
- default:
- return escapedText;
+ if (part.added) {
+ return `${escapedText}`;
+ } else if (part.removed) {
+ return `${escapedText}`;
+ } else {
+ return escapedText;
}
})
.join("");
@@ -112,8 +110,12 @@ export const actions: Actions = {
return {
logId: data.get("id"),
currentDiff: htmlDiff(
- JSON.stringify(log.old_value, null, 2),
+ JSON.stringify(log.old_value, null, 2)
+ .replace(/\\r\\n|\\n|\\r/g, "\n")
+ .replace(/\\\"/g, '"'),
JSON.stringify(log.new_value, null, 2)
+ .replace(/\\r\\n|\\n|\\r/g, "\n")
+ .replace(/\\\"/g, '"')
)
};
}
diff --git a/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte b/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte
index 328db02..48c8c2c 100644
--- a/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte
+++ b/web-app/src/routes/(authenticated)/website/[websiteId]/logs/+page.svelte
@@ -141,9 +141,7 @@
{#if form?.logId === id && form?.currentDiff}
-
{@html form.currentDiff
- .replace(/\\\"/g, '"')
- .replace(/\\r\\n|\\n|\\r/g, "\n")}
+ {@html form.currentDiff}
{/if}
{/if}