render: colourise diff details + widen card, no mid-line wrap

When details is a unified diff (the network-configs notify feeds
`git diff HEAD~1`), colour each line like a pager: additions green,
deletions red, hunk headers violet, file/meta headers dim. Non-diff
details (auto-log output) still render as a plain muted block.

Also widen the card 600->760px and switch the details <pre> from
pre-wrap/word-break to white-space:pre + overflow-x:auto so diff lines
scroll instead of wrapping into mush.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Donavan Fritz
2026-06-15 23:06:47 -05:00
parent 591fb5b5d6
commit 7a9620f9d7
+69 -4
View File
@@ -168,6 +168,71 @@ def fetch_failing_log(
return "\n".join(cleaned[-log_lines:]).strip() return "\n".join(cleaned[-log_lines:]).strip()
# --- diff rendering -------------------------------------------------------
# When `details` is a unified diff (the network-configs notify feeds
# `git diff HEAD~1`), colour each line the way a terminal pager would:
# additions green, deletions red, hunk headers violet, file/meta headers dim.
# Non-diff details (e.g. auto-log output) fall through to a single muted block.
DIFF_ADD = OK_FG
DIFF_DEL = ERROR_FG
DIFF_HUNK = ACCENT_VIOLET
DIFF_META = MUTED
DIFF_CONTEXT = MUTED_STRONG
_HUNK_RE = re.compile(r"^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@")
# Lines that start with +/- but are file headers, not content changes.
_META_PREFIXES = (
"diff --git ",
"index ",
"--- ",
"+++ ",
"new file mode ",
"deleted file mode ",
"old mode ",
"new mode ",
"rename ",
"similarity index ",
"copy ",
"Binary files ",
)
def _looks_like_diff(text: str) -> bool:
"""True if `text` reads as a unified diff (has a git header or a hunk)."""
for ln in text.splitlines():
if ln.startswith("diff --git ") or _HUNK_RE.match(ln):
return True
return False
def _diff_line_color(line: str) -> str:
if _HUNK_RE.match(line):
return DIFF_HUNK
if line.startswith(_META_PREFIXES):
return DIFF_META
if line.startswith("+"):
return DIFF_ADD
if line.startswith("-"):
return DIFF_DEL
return DIFF_CONTEXT
def colorize_diff_html(details: str) -> str:
"""Return inner HTML for the details <pre>: one coloured span per line."""
spans = [
f'<span style="color:{_diff_line_color(line)};">{escape(line)}</span>'
for line in details.split("\n")
]
return "\n".join(spans)
def details_inner_html(details: str) -> str:
"""Colour-highlight diffs; otherwise escape as a plain muted block."""
if _looks_like_diff(details):
return colorize_diff_html(details)
return escape(details)
def fact_rows(items: Iterable[tuple[str, str, str]]) -> tuple[str, str]: def fact_rows(items: Iterable[tuple[str, str, str]]) -> tuple[str, str]:
"""Build the HTML <table> rows and plain-text lines for the fact list. """Build the HTML <table> rows and plain-text lines for the fact list.
@@ -222,8 +287,8 @@ def render(
<pre style="margin:0;padding:14px 16px;background:{CANVAS}; <pre style="margin:0;padding:14px 16px;background:{CANVAS};
border:1px solid {BORDER};border-radius:8px; border:1px solid {BORDER};border-radius:8px;
color:{MUTED_STRONG};font-family:{FONT_MONO}; color:{MUTED_STRONG};font-family:{FONT_MONO};
font-size:12px;line-height:1.55;white-space:pre-wrap; font-size:12px;line-height:1.55;white-space:pre;
word-break:break-word;overflow-x:auto;">{escape(details)}</pre> overflow-x:auto;">{details_inner_html(details)}</pre>
</td></tr> </td></tr>
""" """
@@ -240,8 +305,8 @@ def render(
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" <table role="presentation" width="100%" cellpadding="0" cellspacing="0"
border="0" style="background:{CANVAS};padding:32px 16px;"> border="0" style="background:{CANVAS};padding:32px 16px;">
<tr><td align="center"> <tr><td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" <table role="presentation" width="760" cellpadding="0" cellspacing="0"
border="0" style="max-width:600px;width:100%;background:{SURFACE}; border="0" style="max-width:760px;width:100%;background:{SURFACE};
border:1px solid {BORDER};border-radius:12px; border:1px solid {BORDER};border-radius:12px;
overflow:hidden;"> overflow:hidden;">
<tr><td style="padding:28px 32px 8px 32px;"> <tr><td style="padding:28px 32px 8px 32px;">