mirror of
https://codeberg.org/listyantidewi/your-everyday-tools.git
synced 2026-07-02 07:27:39 +08:00
102 lines
4.3 KiB
HTML
102 lines
4.3 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Text Diff - EveryTools{% endblock %}
|
|
{% block top_title %}Text Diff{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="client-tool">
|
|
<div class="tool-header">
|
|
<h1>Text Diff</h1>
|
|
<p>Compare two texts and see the differences</p>
|
|
</div>
|
|
<div class="split-pane">
|
|
<div class="pane">
|
|
<div class="pane-header"><span>Original</span></div>
|
|
<div class="pane-body">
|
|
<textarea id="diff-left" placeholder="Paste original text..."></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="pane">
|
|
<div class="pane-header"><span>Modified</span></div>
|
|
<div class="pane-body">
|
|
<textarea id="diff-right" placeholder="Paste modified text..."></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-primary" style="margin-top:1rem" onclick="computeDiff()">Compare</button>
|
|
<div id="diff-output" style="margin-top:1rem"></div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<style>
|
|
.diff-view { font-family: "Consolas","Monaco",monospace; font-size: .85rem; border: 1px solid var(--border); border-radius: var(--radius); overflow: auto; max-height: 500px; }
|
|
.diff-line { padding: .15rem .6rem; white-space: pre-wrap; word-break: break-word; display: flex; }
|
|
.diff-num { color: var(--text-light); min-width: 3rem; text-align: right; padding-right: .6rem; user-select: none; flex-shrink: 0; }
|
|
.diff-add { background: #d4edda; }
|
|
.diff-del { background: #f8d7da; }
|
|
.diff-ctx { background: var(--surface); }
|
|
.diff-hdr { background: var(--bg); color: var(--text-light); font-weight: 600; padding: .3rem .6rem; }
|
|
.diff-stats { font-size: .85rem; margin-bottom: .5rem; }
|
|
.diff-stats .add { color: #28a745; font-weight: 600; }
|
|
.diff-stats .del { color: var(--danger); font-weight: 600; }
|
|
[data-theme="dark"] .diff-add { background: #0d2e14; }
|
|
[data-theme="dark"] .diff-del { background: #2e0d0d; }
|
|
[data-theme="dark"] .diff-stats .add { color: #4ecdc4; }
|
|
</style>
|
|
<script>
|
|
function computeDiff() {
|
|
const a = document.getElementById("diff-left").value.split("\n");
|
|
const b = document.getElementById("diff-right").value.split("\n");
|
|
|
|
// Simple LCS-based diff
|
|
const lcs = buildLCS(a, b);
|
|
const lines = [];
|
|
let i = 0, j = 0, li = 0;
|
|
let adds = 0, dels = 0;
|
|
|
|
while (li < lcs.length || i < a.length || j < b.length) {
|
|
if (li < lcs.length) {
|
|
// Output removals before next common line
|
|
while (i < lcs[li][0]) { lines.push({type:"del", num: i+1, text: a[i]}); dels++; i++; }
|
|
while (j < lcs[li][1]) { lines.push({type:"add", num: j+1, text: b[j]}); adds++; j++; }
|
|
lines.push({type:"ctx", num: i+1, text: a[i]});
|
|
i++; j++; li++;
|
|
} else {
|
|
while (i < a.length) { lines.push({type:"del", num: i+1, text: a[i]}); dels++; i++; }
|
|
while (j < b.length) { lines.push({type:"add", num: j+1, text: b[j]}); adds++; j++; }
|
|
}
|
|
}
|
|
|
|
const out = document.getElementById("diff-output");
|
|
let html = `<div class="diff-stats"><span class="add">+${adds} additions</span> <span class="del">-${dels} deletions</span></div>`;
|
|
html += '<div class="diff-view">';
|
|
for (const l of lines) {
|
|
const prefix = l.type === "add" ? "+" : l.type === "del" ? "-" : " ";
|
|
const cls = l.type === "add" ? "diff-add" : l.type === "del" ? "diff-del" : "diff-ctx";
|
|
const escaped = l.text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
|
html += `<div class="diff-line ${cls}"><span class="diff-num">${l.num}</span>${prefix} ${escaped}</div>`;
|
|
}
|
|
html += '</div>';
|
|
if (adds === 0 && dels === 0) html = '<div style="color:var(--success);font-weight:500"><i class="bi bi-check-circle"></i> Texts are identical.</div>';
|
|
out.innerHTML = html;
|
|
}
|
|
|
|
function buildLCS(a, b) {
|
|
const m = a.length, n = b.length;
|
|
const dp = Array.from({length: m+1}, () => new Array(n+1).fill(0));
|
|
for (let i = 1; i <= m; i++)
|
|
for (let j = 1; j <= n; j++)
|
|
dp[i][j] = a[i-1] === b[j-1] ? dp[i-1][j-1]+1 : Math.max(dp[i-1][j], dp[i][j-1]);
|
|
|
|
const result = [];
|
|
let i = m, j = n;
|
|
while (i > 0 && j > 0) {
|
|
if (a[i-1] === b[j-1]) { result.unshift([i-1, j-1]); i--; j--; }
|
|
else if (dp[i-1][j] > dp[i][j-1]) i--;
|
|
else j--;
|
|
}
|
|
return result;
|
|
}
|
|
</script>
|
|
{% endblock %}
|