diff --git a/html/src/app.js b/html/src/app.js
index 13314cb3..a37b6f94 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -5697,6 +5697,138 @@ speechSynthesis.getVoices();
}
});
+ /**
+ * Function that prepare the Longest Common Subsequence (LCS) scores matrix
+ * @param {*} s1 String 1
+ * @param {*} s2 String 2
+ * @returns
+ */
+ $app.methods.lcsMatrix = function (s1, s2) {
+ const m = s1.length;
+ const n = s2.length;
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
+
+ // Fill the matrix for LCS
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ if (s1[i - 1] === s2[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+
+ return dp;
+ };
+
+ /**
+ * Function that find the differences between both strings, and return the differences and their position in the strings.
+ * @param {*} s1 String 1
+ * @param {*} s2 String 2
+ * @returns
+ */
+ $app.methods.findDifferences = function (s1, s2) {
+ const dp = $app.lcsMatrix(s1, s2);
+ const differencesS1 = [];
+ const differencesS2 = [];
+ let i = s1.length;
+ let j = s2.length;
+
+ // Backtrack to find differences
+ while (i > 0 && j > 0) {
+ if (s1[i - 1] === s2[j - 1]) {
+ i--;
+ j--;
+ } else if (dp[i - 1][j] >= dp[i][j - 1]) {
+ differencesS1.push({ index: i - 1, char: s1[i - 1] }); // Deletion in s1
+ i--;
+ } else {
+ differencesS2.push({ index: j - 1, char: s2[j - 1] }); // Insertion in s2
+ j--;
+ }
+ }
+
+ // Remaining characters in s1 (deletions)
+ while (i > 0) {
+ differencesS1.push({ index: i - 1, char: s1[i - 1] });
+ i--;
+ }
+
+ // Remaining characters in s2 (insertions)
+ while (j > 0) {
+ differencesS2.push({ index: j - 1, char: s2[j - 1] });
+ j--;
+ }
+
+ return {
+ differencesS1: differencesS1.reverse(), // Reverse to maintain original order
+ differencesS2: differencesS2.reverse()
+ };
+ };
+
+ $app.methods.findSequeces = function (arr) {
+ if (arr.length === 0) return [];
+ return arr.reduce(
+ (p, c, i) => {
+ if (i === 0) return p;
+ let lastSeq = p.pop();
+ p.push(lastSeq);
+ if (c - lastSeq[1] !== 1) {
+ p.push([c, c]);
+ } else {
+ lastSeq[1] = c;
+ }
+ return p;
+ },
+ [[arr[0], arr[0]]]
+ );
+ };
+
+ /**
+ * Function that format the differences between two strings with HTML tags
+ * markerStartTag and markerEndTag are optional, if emitted, the differences will be highlighted with yellow and underlined.
+ * @param {*} s1
+ * @param {*} s2
+ * @param {*} markerStartTag
+ * @param {*} markerEndTag
+ * @returns An array that contains both the string 1 and string 2, which the differences are formated with HTML tags
+ */
+ $app.methods.formatDifference = function (
+ s1,
+ s2,
+ markerStartTag = '',
+ markerEndTag = ''
+ ) {
+ const texts = [s1, s2];
+ const differs = $app.findDifferences(s1, s2);
+ return Object.values(differs)
+ .map((i) => $app.findSequeces(i.map((j) => j.index)))
+ .map((i, k) => {
+ let stringBuilder = [];
+ let lastPos = 0;
+ let key = Date.now();
+ i.forEach((j) => {
+ stringBuilder.push(texts[k].substring(lastPos, j[0]));
+ stringBuilder.push(
+ `{{diffTag-${key}}}${texts[k].substring(j[0], j[1] + 1)}{{diffTagClose-${key}}}`
+ );
+ lastPos = j[1] + 1;
+ });
+ stringBuilder.push(texts[k].substr(lastPos, texts[k].length));
+ let returnVal = stringBuilder
+ .join('')
+ .replaceAll(/&/g, '&')
+ .replaceAll(//g, '>')
+ .replaceAll(/"/g, '"')
+ .replaceAll(/'/g, ''')
+ .replaceAll(`{{diffTag-${key}}}`, markerStartTag)
+ .replaceAll(`{{diffTagClose-${key}}}`, markerEndTag);
+ return returnVal;
+ });
+ };
+
// #endregion
// #region | App: gameLog
diff --git a/html/src/mixins/tabs/feed.pug b/html/src/mixins/tabs/feed.pug
index 0a345eff..4b0a5e91 100644
--- a/html/src/mixins/tabs/feed.pug
+++ b/html/src/mixins/tabs/feed.pug
@@ -65,10 +65,10 @@ mixin feedTab()
i.x-user-status(:class="statusClass(scope.row.status)")
span(v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'Bio'")
- pre(v-text="scope.row.previousBio" style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0")
- span
- i.el-icon-right
- pre(v-text="scope.row.bio" style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0")
+ template(v-for="(bio,idx) in formatDifference(scope.row.previousBio,scope.row.bio)")
+ pre(v-html="bio" style="font-family:inherit;font-size:12px;white-space:pre-wrap;margin:0 0.5em 0 0")
+ span(v-if="idx===0")
+ i.el-icon-right
el-table-column(:label="$t('table.feed.date')" prop="created_at" sortable="custom" width="120")
template(v-once #default="scope")
el-tooltip(placement="right")