dashboard: per-line color on approval diffs

This commit is contained in:
müde 2026-05-15 16:17:48 +02:00
parent 0f0e242906
commit e2ed58c1a7

View file

@ -249,12 +249,12 @@ async fn render_approvals(approvals: &[Approval]) -> String {
hive_sh4re::ApprovalKind::ApplyCommit => { hive_sh4re::ApprovalKind::ApplyCommit => {
let sha_short = &a.commit_ref[..a.commit_ref.len().min(12)]; let sha_short = &a.commit_ref[..a.commit_ref.len().min(12)];
let diff = approval_diff(&a.agent, &a.commit_ref).await; let diff = approval_diff(&a.agent, &a.commit_ref).await;
let diff_html = render_diff_lines(&diff);
let _ = writeln!( let _ = writeln!(
out, out,
"<li>\n <div class=\"row\"><span class=\"glyph\">→</span> <span class=\"id\">#{id}</span> <span class=\"agent\">{agent}</span> <span class=\"kind\">apply</span> <code>{sha_short}</code>\n <form method=\"POST\" action=\"/approve/{id}\" class=\"inline\"><button class=\"btn btn-approve\" type=\"submit\">◆ APPR0VE</button></form>\n <form method=\"POST\" action=\"/deny/{id}\" class=\"inline\"><button class=\"btn btn-deny\" type=\"submit\">DENY</button></form>\n </div>\n <details><summary>diff vs applied</summary><pre class=\"diff\">{diff}</pre></details>\n</li>", "<li>\n <div class=\"row\"><span class=\"glyph\">→</span> <span class=\"id\">#{id}</span> <span class=\"agent\">{agent}</span> <span class=\"kind\">apply</span> <code>{sha_short}</code>\n <form method=\"POST\" action=\"/approve/{id}\" class=\"inline\"><button class=\"btn btn-approve\" type=\"submit\">◆ APPR0VE</button></form>\n <form method=\"POST\" action=\"/deny/{id}\" class=\"inline\"><button class=\"btn btn-deny\" type=\"submit\">DENY</button></form>\n </div>\n <details><summary>diff vs applied</summary><pre class=\"diff\">{diff_html}</pre></details>\n</li>",
id = a.id, id = a.id,
agent = a.agent, agent = a.agent,
diff = html_escape(&diff),
); );
} }
hive_sh4re::ApprovalKind::Spawn => { hive_sh4re::ApprovalKind::Spawn => {
@ -294,6 +294,33 @@ fn gc_orphans(coord: &Coordinator, approvals: Vec<Approval>) -> Vec<Approval> {
.collect() .collect()
} }
/// Render a unified diff with per-line CSS classes so the dashboard can
/// colour adds / dels / hunk headers / context. Each line becomes a
/// `<span>` tagged by its leading character; the wrapping `<pre>` keeps
/// whitespace intact.
fn render_diff_lines(diff: &str) -> String {
let mut out = String::new();
for raw in diff.lines() {
let cls = match raw.as_bytes().first() {
// file headers (`--- a/...` / `+++ b/...`) come before any
// line starting with a single `+`/`-`. similar-rs emits them
// with the doubled prefix.
_ if raw.starts_with("--- ") => "diff-file",
_ if raw.starts_with("+++ ") => "diff-file",
Some(b'@') => "diff-hunk",
Some(b'+') => "diff-add",
Some(b'-') => "diff-del",
_ => "diff-ctx",
};
let _ = writeln!(
out,
"<span class=\"{cls}\">{}</span>",
html_escape(raw),
);
}
out
}
/// Returns either an empty string (agent is up-to-date / no rev known) or /// Returns either an empty string (agent is up-to-date / no rev known) or
/// a clickable "needs update" badge whose form POSTs to /rebuild/<name>. /// a clickable "needs update" badge whose form POSTs to /rebuild/<name>.
fn update_badge_for(name: &str, current_rev: Option<&str>) -> String { fn update_badge_for(name: &str, current_rev: Option<&str>) -> String {
@ -599,9 +626,15 @@ const STYLE: &str = r#"
overflow-x: auto; overflow-x: auto;
font-size: 0.85em; font-size: 0.85em;
line-height: 1.4; line-height: 1.4;
color: var(--fg); color: var(--muted);
white-space: pre; white-space: pre;
} }
.diff span { display: block; }
.diff .diff-add { color: var(--green); }
.diff .diff-del { color: var(--red); }
.diff .diff-hunk { color: var(--cyan); }
.diff .diff-file { color: var(--purple); font-weight: bold; }
.diff .diff-ctx { color: var(--fg); }
.msgflow { .msgflow {
background: var(--bg-elev); background: var(--bg-elev);
border: 1px solid var(--border); border: 1px solid var(--border);