From c216e287a42715b8999a3bd07550ecd3e2ccbc11 Mon Sep 17 00:00:00 2001 From: Minglong Pan <56749246+minglong51@users.noreply.github.com> Date: Tue, 25 Aug 2026 01:38:14 -0700 Subject: [PATCH] fix(ui): loud clipboard denial, mid-width kill verb, phone legibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the 2026-08-24 dogfood walk (founder-agent-os/prototypes/ dogfood-reports-2026-08-24/agent-usage-manager.md): - bug #1: copyHint had no .catch on navigator.clipboard.writeText — a denied write (permission, unfocused document) failed silently and the operator walked away believing they held a bootout command they didn't. Now flashes "copy failed — press ⌘C" on the chip, then selects the command for a manual copy (an active selection also pauses auto-refresh, so it can't be yanked). Guards the mid-flash re-render race (el.isConnected) so the console stays clean. - bug #2: the 701–~1494px dead zone — the ~1.5kpx nowrap table scrolled the last column (the only kill verb) off-screen while the phone stack only engages ≤700px. The actions column (.c-act) is now position:sticky on the right above the phone breakpoint, right-aligned so the verbs hug the pinned edge (the nowrap launchctl hint inflates the column). Measured at 800px, unscrolled: 0/40 kill buttons visible before, 40/40 + 5/5 launchctl chips after. - F1: phone rows rendered unitless cpu/mem ("1.0 155") — .unit spans, hidden on desk (headers carry the units), shown in the phone stack. - F2: phone child rows lost the cmdline, their only identity — child .c-cmd stays visible in the phone stack, wrapped. - F3: the phone launchd hint ellipsed the job label (the one part that differs) — now truncates from the left (direction: rtl) at 240px, so "…501/ai.hermes.gateway-frontdoor" instead of "launchctl boot…". Out of scope (follow-up): F4, the chronic idle daemon pinned top-row — needs cmdline-granular idle_ok matching in app.py, a config-semantics change. Verified: 75 pytest pass (incl. new test_index_frontend_regressions); browse-verified against an isolated :8766 instance (live :8765 never touched, zero kill clicks): clipboard denial simulated by overriding writeText to reject (flash at t=1ms, restore+select at t=1503ms; success path still flashes "copied ✓"); sticky verbs reachable at 800px and 1440px; phone rows carry units + child cmdlines; hint chips show the job-label tail; console clean. LLD §6 line cites refreshed per the design-doc contract. --- agent_usage_manager/static/index.html | 74 ++++++++++++++++++++++----- docs/design/LLD.md | 37 +++++++++----- tests/test_smoke.py | 24 +++++++++ 3 files changed, 108 insertions(+), 27 deletions(-) diff --git a/agent_usage_manager/static/index.html b/agent_usage_manager/static/index.html index 43ff8da..3b87728 100644 --- a/agent_usage_manager/static/index.html +++ b/agent_usage_manager/static/index.html @@ -27,6 +27,18 @@ td.cmd { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 380px; color: #8b949e; } td.num { text-align: right; font-variant-numeric: tabular-nums; } + /* The all-nowrap table measures ~1.5kpx wide, so between the phone stack + (≤700px) and a full-width window .wrap scrolls horizontally and the last + column — the only kill verb — sits off-screen. Pin the actions column to + the right edge so it's reachable at every width. The nowrap launchctl hint + inflates the column's width, so right-align too: the verbs hug the pinned + edge instead of the cell's left. Phone rows are stacked blocks with no + horizontal overflow, so this is desk/mid-width only. */ + @media (min-width: 701px) { + .c-act { position: sticky; right: 0; background: #0d1117; + border-left: 1px solid #21262d; text-align: right; } + tr.child td.c-act { background: #0b0f14; } + } .label { background: #1f6feb33; color: #79c0ff; padding: 1px 7px; border-radius: 10px; font-size: 12px; } .svc { background: #bb800933; color: #d29922; padding: 1px 7px; margin-left: 5px; @@ -44,6 +56,9 @@ .kids { color: #79c0ff; font-size: 11px; margin-left: 5px; cursor: pointer; border-bottom: 1px dotted #30363d; } .kids:hover { color: #a5d6ff; } + /* Units are hidden on desk — the column headers already say "cpu %"/"mem MB". + The phone stack drops the headers, so there the numbers carry their own. */ + .unit { display: none; } .spark { display: block; margin-top: 3px; } .spark polyline { fill: none; stroke: #d29922; stroke-width: 1.2; } .spark line { stroke: #21262d; stroke-width: 1; } @@ -72,11 +87,19 @@ @media (max-width: 700px) { th, td { padding: 6px 8px; } .c-status, .c-up, .c-cmd, .c-pid { display: none; } /* keep agent/cpu/mem/actions */ + /* …but a child row's cmdline IS its identity (on desk it's what tells the + zsh wrapper from the helper from the MCP server) — keep it, wrapped. */ + tr.child td.c-cmd { display: block; max-width: none; white-space: normal; + overflow-wrap: anywhere; } + .unit { display: inline; } /* headers are gone below — numbers self-label */ header { padding: 10px 12px; gap: 10px; } #stale { padding: 6px 12px; } - /* visually truncate the launchctl hint; click-to-copy still copies the full text */ - code.hint { display: inline-block; max-width: 110px; overflow: hidden; - text-overflow: ellipsis; vertical-align: middle; } + /* Truncate the launchctl hint from the LEFT: every supervised row starts + with the identical 'launchctl bootout gui/501/', so clipping the right + end renders every row's chip the same and ellipses the job label — the + one part that differs. Click-to-copy still copies the full text. */ + code.hint { display: inline-block; max-width: 240px; overflow: hidden; + text-overflow: ellipsis; vertical-align: middle; direction: rtl; } /* The flat table measures ~554px, so at 375px the kill verb sits off-screen. Stack each row instead — agent+badges / cpu·mem / actions — with column headers gone (they no longer map to anything) and the sparkline hidden @@ -109,7 +132,7 @@