v0.21.7.0 feat(ui): workspaces tab redesign — idle collapse, host pills, palette contract#60
Merged
Merged
Conversation
…e contract Open canopy with a dozen known projects and the global Workspaces tab spends most of its real estate showing "(main) not started 4X000 —" rows for projects you aren't working on. Violet was simultaneously the brand pill, active tab, every project header, and the (main) status — the eye couldn't tell brand from section head from cursor. This commit reshapes the tab around what's actually happening on each host. ClassifyIdle (internal/ui/projectlist/projectlist.go) flags projects whose only row is a non-running (main) — status empty or stopped, not broken/orphaned/setting_up. The renderer skips hidden idle rows and emits a per-host "+ N idle projects · e expand" roll-up. `e` toggles the host the cursor is in. j/k/g/G use a nextVisible helper so cursor navigation skips invisible rows. SetRows auto-advances the cursor off any newly-hidden idle row. Host headers become rounded pills (LOCAL, TOWER, PI) on gray-on-darker-gray (245/237), matching the top-bar scope-pill family. A sentinel uninit value for prevHost fixes a pre-existing bug where the local section never got a header when remote hosts existed. Palette contract: violet (99) is now brand-only. selectionStyle moves from grey 237 (which collided with inactiveTabStyle and scopePillStyle) to teal 38. projectHeaderStyle moves from violet to bold-white 231 and indents two spaces under the host pill. mainStatusStyle in render.go moves from violet to dim grey 241. Mem column drops at width < 100, mirroring hosts.Render's D2 tiered-drop policy. Help line auto-includes "e expand idle" via listModeBindings. actionToggleIdleExpand in update_tabs.go is a thin wrapper that forwards the key to projectlist, where the per-host expansion state lives. 12 new tests in projectlist/idle_test.go cover the classifier, the renderer (collapse, expand, no-rollup-when-zero, cursor skip, cursor jump after expand, SetRows auto-advance), hostPill uppercasing, selection-bg and project-header-fg color regression via lipgloss Style getters (TTY-independent), and narrow-width column drop. One new test in render_test.go regression-guards mainStatusStyle. Two existing tests updated for uppercased host names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Problem. The global Workspaces tab on a laptop that knows a dozen projects spends most of its lines on
(main) not started 4X000 —rows for projects you aren't touching. The interesting rows (running workspaces, PR badges, stuck rebases) get buried. The palette compounded it: violet was doing four jobs at once (brand pill, active tab, every project header,(main)status) — the eye couldn't separate brand from cursor from section head.This PR reshapes the tab around what's actually happening on each host.
+ N idle projects · e expandline.ClassifyIdleflags projects whose only row is a non-running(main)(status empty or stopped — broken/orphaned/setting_up stay visible).etoggles the host the cursor is in. Cursor navigation (j/k/g/G) skips hidden rows.mainStatusStyleinrender.gomoves from violet to dim grey 241.hosts.Render's D2 tiered-drop policy.e expand idlechip auto-appears in the help line vialistModeBindings.Test Coverage
12 new tests in
internal/ui/projectlist/idle_test.go:ClassifyIdletable-driven (8 cases): lone main / alive / broken / expanded / loading / per-host independenceTestRender_IdleRollupCollapsedByDefault— collapsed view hides idle, shows+ N idle projectsTestRender_IdleRollupExpandsOnE—eflips collapsed → expanded, hint flips toe collapseTestRender_NoIdleNoRollupLine— zero idle ⇒ no roll-up lineTestNav_SkipsHiddenIdleRows—jdoesn't land on hidden rowTestNav_EReexpandsForJumpToHidden— aftere, previously-hidden row is reachableTestSetRows_CursorOffIdleRow— cursor auto-advances off a hidden rowTestHostPill_Uppercases—tower→TOWERTestSelectionStyle_UsesTeal/TestProjectHeaderStyle_NotViolet— color regression vialipgloss.Stylegetters (TTY-independent)TestRender_NarrowWidthDropsMemColumn—530M 1%drops at width=80, renders at width=120TestNextVisibletable-driven (6 cases)Plus
TestMainStatusStyle_DemotedFromVioletininternal/ui/render_test.go.Two existing tests updated for uppercased host names.
Pre-Landing Review
No critical findings. Diff is UI-only (no SQL, no LLM, no auth, no migrations). Tests are tight and color regressions are guarded against future violet creep.
Test Plan
go test ./...— all packages passgo test -tags=e2e ./...— full suite including E2Ecmd/previewdriver — LOCAL pill, project indent, idle roll-up,etoggle, narrow-width column drop all verified./canopyfrom outside this workspace's nested tmux)🤖 Generated with Claude Code