approvals: ship raw diff text instead of pre-rendered html; client classifies per-line

This commit is contained in:
müde 2026-05-17 12:30:45 +02:00
parent fb669c17c8
commit d48cee7c2d
3 changed files with 29 additions and 37 deletions

View file

@ -736,14 +736,29 @@
denyForm,
);
li.append(row);
if (a.diff_html) {
if (a.diff) {
const details = el('details', {
'data-restore-key': 'approval-diff:' + a.id,
});
details.append(el('summary', {}, 'diff vs applied'));
// diff_html is pre-rendered server-side (per-line class spans inside
// a <pre>); inject as innerHTML.
const pre = el('pre', { class: 'diff', html: a.diff_html });
// Server ships the raw unified diff; classify each line by its
// leading char so `.diff-add` / `.diff-del` / `.diff-hunk` /
// `.diff-file` / `.diff-ctx` colour the output. Building spans
// here (instead of innerHTML-ing pre-rendered markup) keeps
// the snapshot wire format text-only and one less HTML-escape
// surface server-side.
const pre = el('pre', { class: 'diff' });
for (const raw of a.diff.split('\n')) {
let cls = 'diff-ctx';
if (raw.startsWith('--- ') || raw.startsWith('+++ ')) cls = 'diff-file';
else if (raw.startsWith('@')) cls = 'diff-hunk';
else if (raw.startsWith('+')) cls = 'diff-add';
else if (raw.startsWith('-')) cls = 'diff-del';
const span = document.createElement('span');
span.className = cls;
span.textContent = raw + '\n';
pre.appendChild(span);
}
details.append(pre);
li.append(details);
}