Repository navigation
UI rework polish - #2666
UI rework polish#2666
Conversation
Two behaviours the page offered twice, both visible on the same screen. Row hover is opt-in through `clickable-table-row`, and the rule that suppresses it everywhere else was written as `tr:hover > td`. The actors and workers tables are antd `virtual` tables, whose body is divs — no `tr`, no `td` — so those two went on lighting up under the pointer while the pools and templates beside them, and every other static table in the app, had stopped. The rule now matches the cell class antd actually paints the hover from, which exists in both bodies; the pointer and pressed rules for clickable rows are rewritten the same way so the mirror bug cannot appear. Selected rows keep their own hover. The actor and worker columns carried a header of the page's own: a button around the title, a bare arrow beside it, nothing outside those few words to click. It avoided antd's `sorter` because a comparator reorders the page in hand, and one page out of 410,110 reordered is not the cluster sorted. The concern was right and the remedy left a reader two headers to learn. `sorter: true` is antd's header with no comparator behind it, so the whole cell is the target, the chevrons show the direction, and the table still reorders nothing — the order goes out with the next read. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Torey Scheer <[email protected]>
The agent-template form carried no required marker on any field while refusing to save without a name and a model configuration, and the model form's API key was required to create with nothing on screen saying so. Both marks are conditional, because both fields are. The template form renders as text on the details page, where an asterisk would be asking a reader for something the template already has; the model form's API key is needed to create and not to edit, which its label already says in words and now says in the marker too. Every other authoring surface was checked against its own validation rather than for the presence of marks. The harness, MCP server and prompt forms already agreed — including the MCP namespace, which is genuinely optional there and rightly unmarked. A new spec walks all six surfaces and asserts what is marked *and* what is not: a test that only checked the marks would pass just as well on a form that marked every field. It pins the two conditional cases too. `routes` in the Playwright helper gains the two new-resource addresses it was missing. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Torey Scheer <[email protected]>
`suggestName` fell back to `remote-server` or `tool-server` whenever it had no host or package to derive a name from, and the new-server page re-applies the suggestion on every field change while the name is untouched. So opening the wizard and switching to Command wrote `tool-server` into the empty Name field — as did typing a namespace or flipping TLS, with `remote-server`. What had been a placeholder became a value the reader appeared to have chosen. The suggestion is now empty when there is nothing behind it, which is what the field's placeholder is for. Typing a URL or a package still fills the name in, and switching kind now clears a suggestion the other branch cannot support rather than swapping in a made-up one. Two unit tests cover it: the derivation both kinds already had, and the empty case that regressed. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Torey Scheer <[email protected]>
There was a problem hiding this comment.
Reviewed db0b844. All three fixes work.
One blocking concern. Although sorter: true is the right choice here (a comparator would only reorder the current page), the comments written around it credit a server round-trip that never happens. operations.ts sorts the whole inventory in the browser.
Nothing else blocks.
Checked
- ✅ Reverting the hover selector to
> tr…:hover > tdmakes the new spec fail in both browsers (rgb(221, 215, 231)vsrgba(0, 0, 0, 0)). - ✅ The new
<th>header carriestabindex="0",aria-description="sortable",aria-label, andaria-sortonce sorted, and Enter activates it.
🤖 written by Claude
Charlesthebird
left a comment
There was a problem hiding this comment.
I tested this locally as well - I think the red required star on the form fields could say "Required" and be a bit larger.
And the "Remote URL"/"Command" tab buttons on the form are a bit too low contrast.
I have some other design notes/ideas for the UI but won't put them all in this review. Thanks for adding this!
…e rest Eight review comments on kagent-dev#2666, the first of which is that the substrate sort was never server-side. `GetSubstrateStatus` takes a namespace and nothing else, and `localPage` orders the whole inventory in the browser before slicing out a page — so three places asserted a sort that does not happen, one of them text a reader sees: "Sorted by the server: actor, ascending". `sorter: true` stays, because it is right for the opposite reason to the one written down. The read has already ordered every row, so a comparator would re-sort the hundred on screen. The strip beside each table now claims what is true and worth claiming — the order holds across the whole inventory rather than over the page in front of the reader — and `ServerOrder` is `AppliedOrder`, since what it reports is what was applied and not who applied it. Carrying the order in the read key costs a full re-read of the inventory per header click, to reorder rows the browser was just holding. That is now written where the key is built instead of being left to be discovered. Removing it means moving the sort and the slice out of the transport so a reorder can be a re-render, which changes what the operation returns; it belongs in its own change rather than in a header fix. Each column's key is now typed as its sort field, so a key that is not one fails to compile — the shape that let `pod` stand where `workerPod` belonged. Actor `status` and worker `pool` resolve to the same ordering as `default` because they *are* it; named rather than left to fall through, so a change to the default has to decide what those headers mean. Four smaller ones. The template form's name was marked required through a condition that could not be false — it renders only while creating, and the creating caller is never read-only — so it is unconditional and the conditional rationale moves to the model configuration, which is the field it describes. The hover rule's selected-row exception could never match: a row is selectable only where it is also clickable, so this rule has already passed it by. The expand-icon rule still keyed off `tr.clickable-table-row`, the stale selector the rules above it stopped using. And the no-hover test asserted a colour after a fixed wait, which passes just as well when the pointer never arrived — it now asserts antd marked the cell hovered first, so a missed hover fails instead of proving nothing. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Torey Scheer <[email protected]>
I think just the red asterisk is fine personally, since it lets us keep it before the label instead of having to move it after it (plus that much red would be distracting) |
The Remote URL / Command toggle was reported as low contrast in dark mode, and its text was not the problem: the labels measure 12.6:1 selected and 8.2:1 unselected. The control was. Its track measured 1.00:1 against the page — the same colour — and its selected pill 1.12:1 against the track, behind an elevation shadow antd paints at one percent white. Both halves were perfectly legible and nothing said which one was chosen, or that the thing was a toggle. The light theme is left alone, because its pill is white with a dark shadow under it and that reads. Fixed with an edge rather than a fill, the way the inputs are. A fill cannot carry it at this end of the range: the page is near-black, so 3:1 on luminance alone takes a mid-grey pill that reads as disabled. A one-pixel `borderStrong` ring measures 3.5:1 against the track and 3.9:1 against the page, and is the boundary every other control in the app is already outlined with, which leaves the two fills with only the pill's shape to carry. Both toggles on the MCP form are the only segmented controls in the app, and the fix is a theme token, so neither page changes. The spec gains a block of its own for non-text contrast, which is a separate threshold that a check aimed at text walks straight past: 3:1 for the edge that identifies the state, with the labels measured beside it so that edge cannot be won by dimming them. Verified in both directions — without the theme change it reports 1.01:1 and fails. Its shadow colours are composited over what they sit on before being measured. Read as opaque, a one percent white shadow measures as the most visible edge on the page instead of the invisible one it is, which is the mistake this spec's own note already records against page colours. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Torey Scheer <[email protected]>
Head branch was pushed to by a user without write access
Six upstream commits, two of them in the code this branch rewrites. The four conflicts were all in the substrate UI and all artefacts of the squash merge: `UI rework polish (kagent-dev#2666)` landed upstream as one commit while this branch carries it as several, so git could not match the histories. Main's copy of all four files is byte-identical to this branch's own starting point, so taking this side loses nothing — checked by content rather than argued from history. Two upstream changes needed real work rather than a resolution: `fix: wire runtime revisions into substrate inventory (kagent-dev#2703)` replaced the functional-options constructor with a direct `NewService(kubeClient, observedNamespaces, authorizer, ateClient, revisions)`. Upstream's own tests came across converted; the eleven construction sites added on this branch did not, and `go build` does not compile test files, so the tree looked clean while the package would not vet. They now use the new signature. `refactor: standardize Go logging on slog (kagent-dev#2705)` moved the repository off controller-runtime's logger, and `service.go` came out of the merge converted while `substrate.go` — added on this branch, so untouched upstream — was still calling `ctrllog`. It now uses `logging.FromContext(ctx).ErrorContext` with the static lowercase messages and `error` field the new lint rules ask for. kagent-dev#2703's wiring survives the refactor: the harness lookup it restored is still made, now through `actorTemplateHarnesses`, which is what lets a database failure be told apart from an ate-api one. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Torey Scheer <[email protected]>
AgentTemplateFormandModelFormthat were missing itsubstrate tables being more uniform:

agent template /model form inputs being marked required:
