feat: group agents by workspace and nest children#252
Conversation
There was a problem hiding this comment.
parent_id is sourced from the HTTP API because the VPN proto doesn't
carry it. The Agents view backfills it lazily via a per-workspace task
once the menu opens, and VPNMenuState preserves the enriched value
across subsequent VPN upserts.
Why do we need to source parent_id from the HTTP API? Can't we just group agents by workspace? The VPN proto contains workspace_id.
I don't think knowing about the agent<->subagent relationship improves anything here?
|
Also, why are we getting rid of the |
I'm trying out different things. I want to iterate a bit more and share before marking KT ready for review. The idea is to remove the I don't think there is any benefit to keeping .coder, since it's there when you copy or hover over the names. The view would just show you the status, latency, and names of agents along with what workspace they belong to. I think it will remove cognitive toil for the end user. Open to ideas here. |
There are cases like
So this tree hierarchy makes sense. WDYT? |
Fair enough, I don't mind this actually.
However, in the same vain then, I think we should see how this looks and feels in practice. That I would very much prefer if we added As an aside, I'm also not a fan of the aggregated status. I think we should probably just omit it and the copy button for workspaces. Would make it easier to scan the tray at a glance too. |
|
Yes. Once I get a successful live build. I will test. |
|
How are you getting screenshots without a live build? |
Yes the health dot on the top row will go away. |
Mocking them up by lying to UI that VPN is connected 😁 it's a build but without the network extension part. |
The tray shows every agent flat today, which gets unwieldy with many workspaces or workspaces that have sub-agents (devcontainers). Group rows by workspace, and nest child agents under their parent. parent_id is sourced from the HTTP API since the VPN proto doesn't carry it. The Agents view backfills it lazily via a per-workspace task and the menu state preserves the enriched value across subsequent VPN upserts. Single-agent workspaces stay flat so the common case is unchanged.
upsertAgent removes any agent with the same name+wsID before writing the new entry, which was clobbering the enriched parentID we tried to preserve. Read the existing parentID first. Also splits MenuState.swift, VPNMenuItem.swift, and VPNMenuStateTests.swift to satisfy file_length and type_body_length lint rules.
Local swiftformat 0.61 was stricter than CI's 0.55, so my prior commits modified comment styles and access modifiers on lines unrelated to the feature. Revert those to keep the diff strictly additive.
Top-level rows (single-agent flat row, multi-agent header) now show just the workspace name. Nested agent rows show only the agent name. Copy-to-clipboard and the hover tooltip retain the full FQDN so the shell hostname is still one click away. Drops the implicit 8pt VStack gap between top-level workspaces in the ScrollView so the rows match the density of nested children.
…nt-apps toggle When a multi-agent workspace is expanded, every agent now renders inline with its own status dot, latency, copy hostname, and apps grid — no more chevron-to-reveal per agent. Sub-agents (devcontainers) live inside their own dashed "Container" box with the cube glyph leading the row, mirroring the dashboard. Parent apps are hidden when any child has apps, with a square.grid.2x2 toggle to reveal them on demand. Adds a listening-ports menu (dot.radiowaves.right + count) per agent, hidden when the agent reports zero ports per backend contract. Each port opens http://<agent>:<port>. Single-agent rows stay flat and pick up the workspace name as their label; copy/tooltip retain the full FQDN. Tray width bumped to 320 so longer workspace names don't truncate. Devcontainer sub-agents aren't in the workspace endpoint's resources, so the SDK gains workspaceAgent(_:) and workspaceAgentListeningPorts(_:) for per-agent fallbacks. Parent_id resolution moved entirely into expand-time loadApps — the prior eager pass had a sticky dedupe that wouldn't refire after menuState.clear().
Swap the custom collapsible header for `DisclosureGroup` (system chevron + animation, no manual toggle plumbing) and the dashed sub-agent box for `GroupBox` with a "Container" label. Add `Divider()` between top-level workspaces, render system glyphs hierarchically, and bounce the copy icon on each click — same feature surface as the previous render path, just expressed in stock SwiftUI primitives.
Drop the GroupBox label so the cube + agent name share a single row (matches PR #252's compact treatment). The GroupBox still provides the native rounded background that separates each sub-agent from siblings.
- Always render workspaces through DisclosureGroup so single-agent, multi-agent, and offline rows share the system chevron, indent, and hover treatment. Inline empty/offline copy lives in the disclosure content; the label slot carries name, latency, status dot, copy, and the open-in-browser button. - Wrap each sub-agent in a GroupBox instead of the bespoke dashed border. The cube glyph rides on the same row so the container reads in one line. - Apps visibility now flips per-agent through a single override map with sensible defaults (parents collapsed, sub-agents expanded). Toggle shows on any agent with apps, mirroring the dashboard. - Adopt Xcode 26 affordances on the agent row: hierarchical symbol rendering, .symbolEffect(.bounce) on copy, .contentTransition for the apps toggle swap, .draggable(host) for drag-to-terminal, .badge(ports.count) on the ports menu, and .controlSize(.small) on inline buttons. - Empty-state in Agents view uses ContentUnavailableView; consecutive workspaces get a Divider between them.
81cc14e to
8923a65
Compare
The tray currently flattens every agent into a single list using
workspace.agentnotation. With many workspaces — or workspaces thathave sub-agents (devcontainers) — the list gets long and structure-less.
This groups rows by workspace and nests child agents under their parent.
Single-agent workspaces stay flat so the common case is unchanged.
parent_idis sourced from the HTTP API because the VPN proto doesn'tcarry it. The Agents view backfills it lazily via a per-workspace task
once the menu opens, and
VPNMenuStatepreserves the enriched valueacross subsequent VPN upserts.
A view with Mocked Data
Notes
WorkspaceGroup.indentedAgentsandcycle-safe.
(parent_id pointing at an unknown agent) surface at the top level
rather than disappearing.
xcodebuildlocally (no full Xcode), hence the draft— relying on CI to verify the build and tests.