From a91e5a87594b4ba0ad5e215d741ddab4b8e5cec7 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sun, 10 May 2026 08:30:02 -0700 Subject: [PATCH] feat(kanban-dashboard): native
collapse + skip empty metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two follow-up improvements to Tranquil-Flow's metadata-panel restyle. Both stay within the parent PR's "tone down the panel" scope. 1. Native
/ collapse for verbose metadata. The parent PR consciously deferred this ("adding native expand/collapse would be the next step but requires UX agreement"). The default they asked for is straightforward: collapsed when the rendered JSON exceeds 300 chars (the threshold where the max-height: 8.5rem cap actually starts mattering), expanded otherwise.
/ is the right primitive — zero JS, browser-handled state, accessible by default (keyboard-navigable, screen-reader announces the disclosure state), and survives any react-state churn for free. The OS-default disclosure marker is suppressed (list-style: none + ::-webkit-details-marker hidden) and replaced with a CSS ::before chevron that rotates 90deg on the [open] attribute, so the look is consistent across Firefox/WebKit/Blink without the double-marker that would otherwise appear on the platforms that still render the default triangle. 2. Skip rendering when metadata is an empty object. `r.metadata && ...` truthy-checks, but `{}` is truthy in JS — so a completed task with no actual metadata would render a "Metadata" labeled disclosure block containing literal `{}`. Adds an Object.keys(r.metadata).length > 0 guard so empty payloads render nothing instead of an empty disclosure stub. Tests: three new static-asset assertions covering the
shape, the empty-object skip, and the suppress-default-marker + animated-chevron CSS — all in `tests/plugins/test_kanban_dashboard_plugin.py`. --- plugins/kanban/dashboard/dist/index.js | 18 +++++--- plugins/kanban/dashboard/dist/style.css | 19 ++++++++ tests/plugins/test_kanban_dashboard_plugin.py | 46 +++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/plugins/kanban/dashboard/dist/index.js b/plugins/kanban/dashboard/dist/index.js index 71adc77a666..913ff8fe932 100644 --- a/plugins/kanban/dashboard/dist/index.js +++ b/plugins/kanban/dashboard/dist/index.js @@ -2397,12 +2397,18 @@ r.error ? h("div", { className: "hermes-kanban-run-error" }, r.error) : null, - r.metadata - ? h("div", { className: "hermes-kanban-run-meta-block" }, - h("div", { className: "hermes-kanban-run-meta-label" }, "Metadata"), - h("code", { className: "hermes-kanban-run-meta" }, - JSON.stringify(r.metadata, null, 2)), - ) + (r.metadata && Object.keys(r.metadata).length > 0) + ? (function () { + var json = JSON.stringify(r.metadata, null, 2); + var collapsed = json.length > 300; + return h("details", { + className: "hermes-kanban-run-meta-block", + open: !collapsed, + }, + h("summary", { className: "hermes-kanban-run-meta-label" }, "Metadata"), + h("code", { className: "hermes-kanban-run-meta" }, json), + ); + })() : null, ); }), diff --git a/plugins/kanban/dashboard/dist/style.css b/plugins/kanban/dashboard/dist/style.css index 3233facb53c..1179e80d934 100644 --- a/plugins/kanban/dashboard/dist/style.css +++ b/plugins/kanban/dashboard/dist/style.css @@ -867,6 +867,8 @@ * sub-block with a thin left rule, capped height, and muted treatment so * a verbose JSON blob (e.g. changed_files + URLs from a writer task) does * not visually swamp the parent run row or get mistaken for a crash dump. + * Uses a native
/ pair so collapse is browser-handled + * (zero JS); large blobs default collapsed via the open=false attribute. * See issue #19548. */ .hermes-kanban-run-meta-block { margin-top: 0.4rem; @@ -874,6 +876,23 @@ border-left: 2px solid var(--color-border); background: transparent; } +.hermes-kanban-run-meta-block > summary.hermes-kanban-run-meta-label { + cursor: pointer; + list-style: none; +} +.hermes-kanban-run-meta-block > summary.hermes-kanban-run-meta-label::-webkit-details-marker { + display: none; +} +.hermes-kanban-run-meta-block > summary.hermes-kanban-run-meta-label::before { + content: "▶ "; + display: inline-block; + font-size: 0.6rem; + margin-right: 0.25rem; + transition: transform 120ms ease; +} +.hermes-kanban-run-meta-block[open] > summary.hermes-kanban-run-meta-label::before { + transform: rotate(90deg); +} .hermes-kanban-run-meta-label { font-size: 0.65rem; font-weight: 600; diff --git a/tests/plugins/test_kanban_dashboard_plugin.py b/tests/plugins/test_kanban_dashboard_plugin.py index 9eb4ac45a97..8d5ea1afa1b 100644 --- a/tests/plugins/test_kanban_dashboard_plugin.py +++ b/tests/plugins/test_kanban_dashboard_plugin.py @@ -1833,3 +1833,49 @@ def test_run_metadata_secondary_styling(): assert "max-height" in meta_decl assert "overflow: auto" in meta_decl assert "color: var(--color-muted-foreground)" in meta_decl + + +def test_run_metadata_uses_native_collapse(): + """Metadata panel uses
/ for zero-JS collapse. + + Native
means the browser handles state — no event handlers, + no React-state coupling, accessible by default (keyboard navigable, + screen-reader announces the disclosure state). Default-open state is + decided per-render based on payload length. + """ + js = _dashboard_dist_path("index.js").read_text(encoding="utf-8") + # Element must be
/ , not plain
s. + assert 'h("details"' in js + assert 'h("summary"' in js + # The open prop is computed from json length (collapsed when verbose). + assert "open: !collapsed" in js or "open:!collapsed" in js + assert "json.length > 300" in js + + +def test_run_metadata_skips_empty_object(): + """Empty `{}` metadata renders nothing — no useless labeled block. + + `r.metadata && {} && ...` would render a "Metadata" labeled block + containing just `{}`, which is visual noise. The render predicate now + also checks Object.keys(r.metadata).length > 0. + """ + js = _dashboard_dist_path("index.js").read_text(encoding="utf-8") + assert "Object.keys(r.metadata).length > 0" in js + + +def test_run_metadata_disclosure_indicator_styled(): + """Native disclosure marker is hidden + replaced with a CSS-only chevron. + + Browsers render an OS-specific arrow next to by default. For a + consistent look across OSes the hermes dashboard hides that marker and + renders a CSS ::before chevron that rotates on [open]. Pin it so a + future CSS rebuild can't silently lose it (which would put two markers + side-by-side on Firefox/WebKit). + """ + css = _dashboard_dist_path("style.css").read_text(encoding="utf-8") + # Default markers suppressed. + assert "list-style: none" in css + assert "::-webkit-details-marker" in css + # CSS-only chevron present + animates on open state. + assert ".hermes-kanban-run-meta-block[open]" in css + assert "rotate(90deg)" in css