From 77e9a8c2362789b72ab5ed79e64f07a015ee452a Mon Sep 17 00:00:00 2001 From: Pierre Paridans Date: Tue, 25 Nov 2025 00:36:36 +0100 Subject: [PATCH] [REF] runbot: tracking_value revamp Globally, a cleanup of the tracking value diff implementation: - avoid multiple (many?) diff recompute using memoization - uses CopyButton component - modernize a bit the JavaScript syntax - "Bootrapify" and refine diff rendering - diff's line highlight on hover is back --- runbot/static/src/js/fields/tracking_value.js | 128 +++++++++--------- .../static/src/js/fields/tracking_value.scss | 56 +++++--- .../static/src/js/fields/tracking_value.xml | 37 +++-- 3 files changed, 124 insertions(+), 97 deletions(-) diff --git a/runbot/static/src/js/fields/tracking_value.js b/runbot/static/src/js/fields/tracking_value.js index 14058b1cd..5e40e67dd 100644 --- a/runbot/static/src/js/fields/tracking_value.js +++ b/runbot/static/src/js/fields/tracking_value.js @@ -1,74 +1,78 @@ /** @odoo-module **/ import { patch } from "@web/core/utils/patch"; import { Message } from "@mail/core/common/message"; +import { CopyButton } from "@web/core/copy_button/copy_button"; +import { memoize } from "@web/core/utils/functions"; + +const diffMatchPatch = new diff_match_patch(); + +function makeDiff(text1, text2) { + const { chars1, chars2, lineArray } = diffMatchPatch.diff_linesToChars_(text1, text2); + const diffs = diffMatchPatch.diff_main(chars1, chars2, false); + diffMatchPatch.diff_charsToLines_(diffs, lineArray); + diffMatchPatch.diff_cleanupSemantic(diffs); + return diffs; +} + +function prepareForRendering(diffs) { + const lines = []; + let preLineCounter = 0; + let postLineCounter = 0; + for (const { 0: diffType, 1: data } of diffs) { + for (let line of data.split("\n")) { + line = line.replace(/&/g, "&"); + line = line.replace(//g, ">"); + //text = text.replace(/\n/g, "
"); + //text = text.replace(/ /g, "  "); + let type; + if (diffType === -1) { + type = "removed"; + preLineCounter += 1; + } else if (diffType === 0) { + type = "kept"; + preLineCounter += 1; + postLineCounter += 1; + } else if (diffType === 1) { + type = "added"; + postLineCounter += 1; + } + lines.push({ type, preLineCounter, postLineCounter, line }); + } + } + return lines; +} + +function computeDiff({ oldValue, newValue }) { + const diff = makeDiff(oldValue, newValue); + return prepareForRendering(diff); +} + +patch(Message, { + components: { + ...Message.components, + CopyButton, + }, +}); patch(Message.prototype, { setup() { super.setup(...arguments); - this.kept = false; + this.state.showKept = false; }, - isMultiline(trackingValue) { - const oldValue = trackingValue.oldValue; - const newValue = trackingValue.newValue; - return ((oldValue && typeof oldValue=== 'string' && oldValue.includes('\n')) && (newValue && typeof oldValue=== 'string' && newValue.includes('\n'))) - }, - formatTracking(trackingFieldInfo, trackingValue) { - return super.formatTracking(trackingFieldInfo, trackingValue) + + lines: memoize(computeDiff), + + isMultiline({ oldValue, newValue }) { + return ( + typeof oldValue === "string" && + oldValue.includes("\n") && + typeof oldValue === "string" && + newValue.includes("\n") + ); }, + toggleKept() { - this.kept = !this.kept; - }, - copyToClipboard(trackingValue) { - return function () { - navigator.clipboard.writeText(trackingValue); - }; + this.state.showKept = !this.state.showKept; }, - lines(trackingValue) { - const oldValue = trackingValue.oldValue; - const newValue = trackingValue.newValue; - const diff = this.makeDiff(oldValue, newValue); - const lines = this.prepareForRendering(diff); - return lines; - }, - makeDiff(text1, text2) { - var dmp = new diff_match_patch(); - var a = dmp.diff_linesToChars_(text1, text2); - var lineText1 = a.chars1; - var lineText2 = a.chars2; - var lineArray = a.lineArray; - var diffs = dmp.diff_main(lineText1, lineText2, false); - dmp.diff_charsToLines_(diffs, lineArray); - dmp.diff_cleanupSemantic(diffs); - return diffs; - }, - prepareForRendering(diffs) { - var lines = []; - var pre_line_counter = 0 - var post_line_counter = 0 - for (var x = 0; x < diffs.length; x++) { - var diff_type = diffs[x][0]; - var data = diffs[x][1]; - var data_lines = data.split('\n'); - for (var line_index in data_lines) { - var line = data_lines[line_index]; - line = line.replace(/&/g, '&'); - line = line.replace(//g, '>'); - //text = text.replace(/\n/g, '
'); - //text = text.replace(/ /g, '  '); - if (diff_type == -1) { - lines.push({type:'removed', pre_line_counter: pre_line_counter, post_line_counter: '-', line: line}) - pre_line_counter += 1 - } else if (diff_type == 0) { - lines.push({type:'kept', pre_line_counter: '', post_line_counter: post_line_counter, line: line}) - pre_line_counter += 1 - post_line_counter +=1 - } else if (diff_type == 1) { - lines.push({type:'added', pre_line_counter: '+', post_line_counter: post_line_counter, line: line}) - post_line_counter +=1 - } - } - } - return lines; - }, }); diff --git a/runbot/static/src/js/fields/tracking_value.scss b/runbot/static/src/js/fields/tracking_value.scss index d548e82f7..32893eb5b 100644 --- a/runbot/static/src/js/fields/tracking_value.scss +++ b/runbot/static/src/js/fields/tracking_value.scss @@ -1,29 +1,43 @@ -.code_diff { - white-space: nowrap; - overflow-x: auto; - font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; +.code-diff { + .table { + > tbody { + font-family: var(--font-monospace); + font-size: 0.75rem; + line-height: 1; + white-space: nowrap; + } - .col_number { - background-color: #EEE; - } + > :not(caption) > * { + border-width: 0 var(--border-width); - .added { - background:#a1f1a1; - } - - .removed { - background:#f3a3a3; + &:first-child { + border-top-width: var(--border-width); + } + + &:last-child { + border-bottom-width: var(--border-width); + } + + > * { + border-width: 0; + padding: 0.3rem; + + &.diff-line-sign { + border-left-width: var(--border-width); + } + } + } } - - .code { - white-space: pre-wrap; + + .diff-line-number { + width: 4ch; } - .diff_line { - background-color: #FFF; + + .diff-line-sign { + width: 2ch; } - .diff_line:hover { - filter: brightness(92%); + .diff-line-code { + white-space: pre-wrap; } - } diff --git a/runbot/static/src/js/fields/tracking_value.xml b/runbot/static/src/js/fields/tracking_value.xml index ae8e3eecf..a8a365a6d 100644 --- a/runbot/static/src/js/fields/tracking_value.xml +++ b/runbot/static/src/js/fields/tracking_value.xml @@ -4,21 +4,30 @@
  • -
    - - - +
    + + +
    -
    ()
    -
    - - - - - +
    +
    - - -
    + + + + + + + + + +
    ()
    + - + + + +