From 7d0249ed513f1bb1a06baf0110b713f21e688c76 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 29 Sep 2026 11:05:48 +0200 Subject: [PATCH] UI: fold finished steps to their title line with a toggle triangle Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 295952@bnt-lap001 Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477 --- rtut/ui.py | 11 ++++++----- tests/test_ui.py | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/rtut/ui.py b/rtut/ui.py index 0da1f9b..2685905 100644 --- a/rtut/ui.py +++ b/rtut/ui.py @@ -19,7 +19,7 @@ h1,h2{font-weight:600}a{color:inherit}hr{border:0;border-top:1px solid #8886} .step{border:1px solid #8886;padding:.8rem 1rem;margin:.8rem 0}.step.running{border-color:currentColor} .pill{font:12px ui-monospace,monospace;border:1px solid #8886;padding:0 .4rem} pre{border:1px solid #8886;padding:.5rem;overflow:auto;margin:.4rem 0} -button,input,select{font:inherit;padding:.25rem .5rem}.head{display:flex;justify-content:space-between;align-items:baseline;gap:1rem}.row{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap}.grow{flex:1;min-width:12rem}[hidden]{display:none}#current{scroll-margin-top:1rem}.note{opacity:.75}.bad{font-weight:600}""" +button,input,select{font:inherit;padding:.25rem .5rem}.head{display:inline-flex;width:calc(100% - 1.6rem);justify-content:space-between;align-items:baseline;gap:1rem;vertical-align:top}summary{cursor:pointer}.row{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap}.grow{flex:1;min-width:12rem}[hidden]{display:none}#current{scroll-margin-top:1rem}.note{opacity:.75}.bad{font-weight:600}""" def page(title: str, body: str) -> bytes: @@ -106,7 +106,7 @@ class App: if cmd and state == "running": # the command and Copy appear only once the step is started block = f"
{e(cmd)}
" elif cmd and state == "done": - block = f"
Command
{e(cmd)}
" + block = f"
{e(cmd)}
" # kept as a record; no Copy once done url = f"/run/{i}/{e(rid)}/{e(s['id'])}" v = st.get("verification") if state == "todo" and not blocked: @@ -148,11 +148,12 @@ class App: waiting_for = f"step {n}, {s['title']}" rb = f"

Rollback: {e(s['rollback'])}

" if s.get("rollback") and state in ("running", "failed") else "" anchor = " id=current" if s["id"] == current else "" - out.append(f"
" + fold = "" if state in ("done", "skipped") else " open" # finished steps start folded; the triangle toggles + out.append(f"" f"{n}. {e(s['title'])} " f"[{e(s['owner'])}] {e(s.get('risk', 'read-only'))}" - f"{state}
{block}" - f"

Done when: {e(s['verify']['done_when'])}

{rb}{act}") + f"{state}{block}" + f"

Done when: {e(s['verify']['done_when'])}

{rb}{act}") if r.complete: out.append("

All steps done. To record this run as exercising the pack, review and apply: " f"python -m rtut run finish {e(p.path)} {e(rid)} --apply

") diff --git a/tests/test_ui.py b/tests/test_ui.py index 7f531fb..d405e15 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -131,7 +131,7 @@ def test_command_and_copy_appear_only_when_step_is_started(srv): req(srv, "POST", base + "/read-the-sign/done", {"_t": t, "note": "read it twice"}) _, _, body = req(srv, "GET", base) assert "