Skip to content

Workflow-first UX redesign and TUI polish - #1

Merged
harder merged 43 commits into
mainfrom
winget-tui-redesign
May 24, 2026
Merged

harder merged 43 commits into
mainfrom
winget-tui-redesign

Conversation

@harder

@harder harder commented May 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • redesign SkillView around a workflow-first shell with Discover, Installed, Changes, and Doctor workspaces
  • tighten the TUI with cleaner Discover details, safer install/remove/cleanup flows, stabilized Installed layout, compact agent labels, and updated README/docs
  • remove the obsolete workflow-first implementation plan doc now that the redesign is shipped

Test Plan

  • dotnet build SkillView.sln --no-restore --nologo
  • dotnet test SkillView.sln --no-build -m:1 --nologo

harder and others added 30 commits May 17, 2026 22:35
Introduce a truecolor palette mirroring shanselman/winget-tui (gold accent
#EEC98D, warm beige #E8DCB7, dark surfaces, mint success, red danger, blue
info, purple selection) and register it under the built-in "Base" and
"Dialog" scheme names at app start. TuiHelpers status / editable / read-only
/ table schemes delegate to the new palette for AppTheme.Default; HighContrast
keeps its existing 16-color StandardColor schemes for terminal-default
fidelity.

Phase 1 of the winget-tui redesign tracked in
.claude/plans/do-a-deep-analysis-harmonic-cascade.md.
…se 2)

Pull the right-pane construction (item-actions strip, Details metadata frame,
SKILL.md preview + raw fallback, log overlay) into a dedicated
SkillDetailPaneView : FrameView. SkillViewApp keeps its private references to
the inner controls so existing render/toggle methods are unaffected — this is
purely a structural extraction. Behavior-bearing methods (UpdateMetadataPane,
SetPreviewText, TogglePreviewMode) stay on SkillViewApp and will migrate in
later phases as call sites stabilize.

Also shifts the main left/right split from 50/50 → 60/40 to match winget-tui's
package-list/detail proportions. Long repo names get more room without
sacrificing wrapped SKILL.md prose readability.

Phase 2 of the winget-tui redesign.
Introduce a persistent top-header tab bar (◇ Search · ▣ Installed · △ Updates)
in the winget-tui accent palette. Renders the active tab in bold gold and
inactive tabs dim; click hit-testing per pill via OnMouseEvent.

Wire 1/2/3 direct-jump and ←/→ cyclic tab keys. Search stays the embedded
base view; Installed and Updates currently route to the existing modal
screens (ShowInstalled / ShowUpdateScreen) — Phases 4 and 5 will replace
those modals with embedded tab views. After modal close the active-tab
indicator snaps back to Search so the header stays honest about what's
visible.

Phase 3 of the winget-tui redesign.
Introduce a winget-tui-style modal keybinding reference rendered as grouped
Markdown — Navigation, Search & filter, Actions, Batch, Modes — accessible
via either `?` or F1. Dismisses on Esc / Enter / `?` / Close button.

Bind `?` at the window-shortcut handler alongside F1 and replace the
single-line MessageBox.Query help with the new HelpOverlay. Promote `?` in
the preview status bar in place of the redundant F1 label.

Phase 7 (partial — key map centralization deferred until the embedded
Installed/Updates tabs land, since the central key map is the right home for
their per-tab hint generation).
…se 6a)

Adopt winget-tui's hybrid install vocabulary:

  i — InstallConfirmModal (one screen: Scope radio · agents row · Install
      button) with sensible defaults (Project scope if the cwd has a known
      agent seed, otherwise User; agents pre-checked from
      InstallAgentCatalog.DetectInstalledGhIds against HOME).
  I — direct entry into the existing multi-step InstallScreen wizard.

The compact modal exposes an Advanced… button that escalates to the wizard
without losing the picked scope. Coordinator gains a forceAdvanced parameter
that bypasses the compact step for I; both paths flow through the same
post-install inventory rescan and success/failure status reporting.

BuildOptionsFromSelection is pulled out as a pure helper so the
scope→options mapping can be unit-tested without standing up Terminal.Gui
views (4 new tests, 379/379 unit + 1/1 integration green).

Phase 6a of the winget-tui redesign. (RemoveConfirmModal follows.)
Add a winget-tui-style "Remove {name}? [y/n]" modal that takes over the
simple-remove path. The coordinator builds the targets via
RemoveTargetResolver and routes to the compact confirm only when
CanRunCompact(eval) holds — a single CurrentInstall target with no
validation errors and no second-confirm flag. Anything that touches a
package group, repo group, agent symlink, or trips a containment / incoming-
symlink rule keeps the existing multi-step RemoveScreen wizard.

The compact modal exposes an Advanced… button (and `a` key) for users who
want the full review surface even on a simple skill.

CanRunCompact is a pure predicate over RemoveTargetEvaluation, covered by
three new tests that exercise the simple-pass, blocked-by-error, and
agent-symlink paths (382/382 unit + 1/1 integration green).

Phase 6 of the winget-tui redesign complete.
Add a monotonic _searchGeneration counter incremented on every
RunSearchAsync invocation. The Invoke callback that paints results checks
its captured generation against the current one and drops stale completions
silently — mirrors winget-tui's app.view_generation pattern (src/app.rs) so
out-of-band results from earlier searches can never clobber a newer query's
view.

Status bar: replace the stale `I → Installed` label (Installed is now
reached via `2` jump or ←/→ cycling) with the new winget-tui-flavored
shortcuts: `1/2/3 → Tabs`, `i → Install` (compact), `I → Install…` (advanced
wizard). Other labels unchanged. Discoverability of the full key map stays
in the `?` HelpOverlay.

Phase 8a (refinements — first slice).
Convert the InstalledScreen Application.Run(window) subloop into an
embedded InstalledTabView : FrameView that lives inside the main window
with Visible=false until tab 2 is activated. Activation flips visibility
on the search frames and kicks off an inventory snapshot load via the
coordinator's CaptureInventorySnapshotAsync. Startup auto-open seeds the
tab from the already-captured snapshot to avoid double-scanning.

The view delegates key dispatch + detail rendering to the existing static
helpers on InstalledScreen (DecideShortcut / BuildShortcuts / RenderDetail
/ ShortcutCommand) so the 7 InstalledScreenTests stay green unchanged. The
modal Show() method, ShowInstalled/OpenInstalledSnapshot coordinator
methods, and the dead private SortMode/SortLabel/BuildFooter helpers are
removed.

Esc/q on the tab now switches back to Search (instead of tearing down a
subloop); `/` invokes the search field with focus restored.

382/382 unit + 1/1 integration green.

Phase 4 of the winget-tui redesign complete.
Convert the UpdateScreen Application.Run(window) subloop into an embedded
UpdatesTabView : FrameView. Activation of tab 3 (via `3`, `→`, or `u`)
hides the Search/Installed panes, reveals the Updates tab, and kicks off
an inventory snapshot load via CaptureInventorySnapshotAsync.

Adopt winget-tui's batch vocabulary on top of the existing
CheckBoxTableSourceWrapperByIndex:
  Space — toggle mark on current row (already wired by the wrapper)
  a     — mark all visible rows
  A     — unmark all
  u     — update current row only (single skill)
  U     — update every marked row (batch)
  d     — dry-run
  Esc   — back to Search tab

Capability-gated checkboxes (--all / --force / --unpin / --yes) and the
Dry-run / Update buttons stay; on success the tab invalidates the cached
gh skill list so the next Installed-tab view shows fresh data.

Strip the modal UpdateScreen.Show() body and the coordinator's
ShowUpdateScreen wrapper. Keep UpdateScreen.RenderResult as a static helper
so UpdateScreenTests (3 tests) stay green unchanged. The `u`/`U` legacy
window shortcuts now route to ActivateTab(Updates) instead of the old
modal.

382/382 unit + 1/1 integration green.

Phase 5 of the winget-tui redesign complete — all three tabs are now
embedded; no more modal subloops for the primary workflows.
…se 8b/1)

Search tab — `S` (or `s`) cycles SearchSort: Off (gh order) → StarsDesc →
NameAsc → NameDesc → RepoAsc → Off. Status bar reports the active mode
("sort: stars ↓" etc.). _resultsNaturalOrder preserves the natural gh-skill-
search ranking so Off restores the original sequence rather than a stale
sort. Mirrors winget-tui's app.sort_field cycle in src/app.rs.

Installed tab — `P` (capital) cycles PinFilter: All → PinnedOnly →
UnpinnedOnly → All. Footer surfaces "📌 pinned only / unpinned only" when
the filter is engaged; the per-tab StatusBar adds a "P · Pin filter" entry
via a small WithPinShortcut insertion helper. P only fires when the filter
text field doesn't own focus so typing literal P in the filter still works.

Cycle helpers are pure static methods: SkillViewApp.{CycleSearchSort,
ApplySearchSort, DescribeSearchSort} and InstalledTabView.{CyclePin,
DescribePin}. 7 new tests cover round-trip cycles, stars-desc null
ordering, case-insensitive name sort, and the Off-mode order-preservation
invariant. 389/389 unit + 1/1 integration green.

Phase 8b first slice.
Convert DoctorScreen.Show()'s Application.Run(window) subloop into
DoctorTabView : FrameView. Activated by `d` via EnterDoctor() which hides
all primary tabs (Search / Installed / Updates) and reveals the Doctor pane
full-screen. LeaveDoctor() restores the tab the user was on before — not
always Search — by remembering _tabBeforeDoctor at entry time.

Doctor isn't a primary tab so it gets no pill in TabBarView; users still
reach it via `d` (consistent with winget-tui's modal-like power-user views).
Esc / q dismisses. Lazy gh probe is preserved: if no EnvironmentReport has
been captured yet, the pane reveals immediately and the report renders
once the background probe completes.

Strip the modal Show() body from DoctorScreen and the coordinator's
ShowDoctor wrapper; keep DoctorScreen.Render as a static helper so the 3
DoctorScreenTests pass unchanged. 389/389 unit + 1/1 integration green.

Phase 8b second slice — every primary view in Skill View is now embedded;
the only remaining Application.Run(window) subloops are in escalation
modals (InstallScreen wizard, RemoveScreen wizard, CleanupScreen) which
retain their modal semantics intentionally.
Suffix the active sort column's header with ↑/↓ ("Name ↓", "★ ↓",
"Repo ↑") so the user sees what `S` last cycled to without consulting the
status line. Off-mode (gh's natural order) shows bare "★ / Name / Repo".

TuiHelpers.ApplyColumnStyles now matches on the leading whitespace-
delimited token so the existing Skill/Name/Repo/★ width rules keep applying
when a trailing glyph is present.
Rewrite the TUI section to describe the current Search / Installed /
Updates tab layout, persistent detail pane, and Doctor as a full-screen
overlay. Replace the single keyboard table with grouped Navigation /
Search / Installed / Updates / Other tables that match what each tab
actually accepts (1/2/3 jumps, ←/→ cycling, ? help, S sort, P pin filter,
Space/a/U batch select, i compact vs I advanced install).

Note the developer-architecture detail that primary workflows are embedded
views (no Application.Run subloops) while escalation wizards keep modal
semantics. Add Tabs/ and Theming/ to the project layout table.

Keep all CLI / install / troubleshooting / automation sections intact —
they were already accurate.
Survey of the 118 develop commits between v2.1.0 and v2.2.0-rc.3:

Adopted for free (no code changes — bug-fix / behavior wins):
- MarkdownView: selection-overlay bugs fixed (#5270/#5273/#5281); Ctrl+Click
  on link no longer pops the context menu (#5283); right-click adds a
  "Copy Link" entry (#5285); OSC 8 stale-row clears and re-open lifecycle
  reset (#5275/#5276).
- Button delimiters now anchor to edges; highlight continuity fixed (#5279).
- KeystrokeNavigator automatically syncs to Source when reassigned — paths
  through DisableTypeToSearch (CollectionNavigator = null) are unaffected.
- New code-token VisualRoles (CodeComment / CodeKeyword / CodeString / …)
  with sensible defaults derive automatically from our Base scheme — code
  fences in SKILL.md previews get token coloring at no cost.
- Accent scheme constructor preserves code roles from its base (#review).

Required source change:
- TextView, TextViewAutocomplete, ContentsChangedEventArgs marked
  [Obsolete] in favor of gui-cs/Editor's EditorView. SkillView uses
  TextView only as a read-only scrollable surface for the raw SKILL.md
  preview pane and the log overlay — neither needs an editing model. Adding
  a second NuGet for two read-only panes isn't justified, so suppress
  CS0618 narrowly in the three call sites (SkillDetailPaneView,
  TuiHelpers.ConfigureReadOnlyPane, the field decls in SkillViewApp) plus
  the matching unit-test fixture. The pragma is paired with a comment
  explaining the choice so a future maintainer reading "obsolete" knows
  the migration is intentional.

Not relevant to SkillView: ImageView/Sixel rendering, ColorBar mouse
refactor, FileDialog End key fix, editor-oriented Command additions
(Find/Replace/InsertTab/FindNext/...), AppTestHelper.RunAsync refactor
(our integration tests don't use it).

PackageReferenceTests and VersionFlag_IncludesTerminalGuiVersion bumped
to match. Assembly version surfaces as "2.2.0" at runtime (the package
"-rc.3" suffix is stripped); test assertion uses the SemVer triplet.

389/389 unit + 1/1 integration green on the new TG.
cli/cli#13245 is being closed by cli/cli#13418 (Tommy/skills list), which
adds `gh skill list` with the canonical shape:

  { skillName, hosts[], scope, sourceURL, version, pinned, path }

Where scope ∈ {project, user, custom}, hosts is always an array (empty for
--dir / custom scans, multi-valued for shared install dirs like
.agents/skills), and sourceURL is a full GitHub URL or local filesystem
path. The PR is still open, so this change keeps SkillView's tolerant
parser tolerant — upstream-canonical keys take priority but every legacy /
alternate key our adapter previously recognized stays as a fallback.

Adapter changes:
- GhSkillListRecord adds SourceUrl (string) and Hosts (ImmutableArray) as
  first-class fields; the existing Agents property is now an alias of
  Hosts so any call site that references it continues to work.
- ReadRecord reads `skillName` → fallback `name`/`skill_name`; `hosts` →
  fallback `agents`; `sourceURL` → fallback `repo`/`repository`. New
  ReadStringArray helper deduplicates the loop body.
- CapabilityProbeParser re-enables the `list` probe with the actual
  upstream flag set (`--json --agent --scope --dir`). When the gh binary
  doesn't ship the subcommand the probe is a no-op — ListSubcommandPresent
  stays false and SkillView's filesystem scan stays in charge.
- CapabilityProfile.SupportsListDir lights up automatically once the gh
  binary exposes the flag — unused today but available to any future tab
  that wants to scan a custom dir via gh.

Merge changes:
- CliList-only InstalledSkill records propagate SourceUrl into
  FrontMatter.Upstream so detail panes can render a clickable source link
  for skills that gh knows about but the filesystem scan hasn't seen.
- Agent membership is built from Hosts (upstream-canonical) instead of
  Agents; the legacy singular `agent` field still seeds a single
  membership when no array is present.

Tests:
- GhSkillListAdapterTests adds canonical-payload, multi-host, custom-dir,
  and precedence cases (hosts beats agents, skillName beats name).
- CapabilityProbeParserTests asserts the list-probe flag set instead of
  asserting the subcommand absent.

393/393 unit + 1/1 integration green. Nothing changes behaviorally on a
gh that doesn't ship `gh skill list` yet — the alignment lands now so when
PR #13418 merges, SkillView picks the canonical fields up automatically.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Swap the raw preview and log panes from TextView to Terminal.Gui.Editor.Editor and keep them configured as read-only viewer surfaces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ContextBarState record and ContextBarView with FormatForTests helper.
  Uses 'Locations' wording (never 'roots') per spec §50.
- Add StatusHint record and StatusStripView with TruncateHintsForTests helper.
  Rightmost shortcut pairs survive truncation when space is tight.
- Add TuiHelpers.ApplyFocusFrameStyle(FrameView, bool) that applies
  Heavy border for focused panes and Rounded for unfocused.
- Tests: ContextBarViewTests, StatusStripViewTests, and three new
  TuiHelpersTests (ApplyFocusFrameStyle); all 69 target tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three bugs caught in code-quality review of Task 1:

1. Right hints could overwrite centered status text on narrow widths.
   Added ComputeHintLeftEdge() to calculate the right-hint budget as
   width minus the farther of the left-badge end or the center-status
   end position.  The budget is now passed to TruncateHintsForTests so
   hints are always allocated space that does not overlap the status.

2. Exactly-fitting hint text was suppressed by the rx > x guard.
   Changed to rx >= leftEdge so a hint block that lands right at the
   safe-start boundary is drawn instead of silently dropped.

3. StatusStripView hardcoded WingetTuiTheme palette colors directly,
   bypassing the HighContrast / Default gate used everywhere else.
   Added TuiHelpers.GetStatusStripAttributes() following the same
   CurrentTheme branch pattern as GetStatusScheme/ConfigureTableScheme,
   and updated OnDrawingContent to call it.

Tests added:
- TruncateHintsForTests_ExactFit_IncludesHint (Bug 2 regression)
- TruncateHintsForTests_OneLessThanExact_DropsHint (boundary)
- ComputeHintLeftEdge_NoStatusText_ReturnsLeftBadgeEnd (Bug 1)
- ComputeHintLeftEdge_WithStatusText_ReturnsEndOfCenterRegion (Bug 1)
- ComputeHintLeftEdge_LeftBadgesExceedCenterEnd_ReturnsLeftBadgeEnd
- ComputeHintLeftEdge_LongStatusText_ClampedToHalfWidth
- GetStatusStripAttributes_DefaultTheme_UsesWingetTuiSurfaceBackground (Bug 3)
- GetStatusStripAttributes_HighContrastTheme_UsesBlackBackground (Bug 3)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the top-level tab enum from Search/Installed/Updates to
Discover/Installed/Changes and update all shell wiring in SkillViewApp
to match the new names.

- TabBarView: rename SkillViewTab enum values and pill labels; add
  TabLabelsForTests helper
- Tabs/DiscoverTabView.cs: minimal FrameView skeleton (placeholder
  for Task 3 content extraction)
- Tabs/ChangesTabView.cs: minimal FrameView skeleton (placeholder
  for Task 3 content extraction)
- SkillViewApp: update every SkillViewTab.Search → Discover and
  SkillViewTab.Updates → Changes reference; instantiate and add the
  two skeleton views to the window graph; expose ChangesTabForTests
  and TabBarForTests test hooks; keep UpdatesTabView as the adapter
  for Changes tab content until the next extraction task
- SkillViewAppTests: fix BuildUi_WindowShortcutFocusesQueryField
  assertion to expect Discover; add three new tests
  (BuildUi_DefaultsToDiscoverTab, BuildUi_ExposesChangesTabHook,
  BuildUi_TabBarUsesWorkflowFirstLabels)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the search shell (query fields, results table, detail pane) out of
SkillViewApp into DiscoverTabView using the adapter pattern established
by SkillDetailPaneView — all inner controls are exposed as internal
properties so existing event-handler and rendering call sites in
SkillViewApp continue to work unchanged.

- DiscoverTabView: rewrite from skeleton into full search workspace;
  owns LeftFrame (search controls + TableView) and SkillDetailPaneView
  (right detail pane) in a persistent 60/40 layout; applies scheme to
  all owned controls; exposes BuildFacetSummaryForTests static helper
  that uses Locations: wording (not roots) and includes hidden-dir state
- SkillDetailPaneView: add SetMetadataChips(params string[] chips) helper
  for chip-style metadata updates
- SkillViewApp.BuildUi: replace 120-line inline search scaffold with a
  single DiscoverTabView construction; pull all control references back
  into existing fields so the rest of the file is unchanged; update
  ShowSearchPanes to toggle _discoverTab.Visible instead of separate
  left/right frame visibility; window.Add no longer adds _leftFrame and
  _rightFrame directly (they live inside DiscoverTabView now)
- Tests: add DiscoverTabViewTests covering Locations wording, hidden-dir
  state, and empty-field omission

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SetMetadataChips was added to SkillDetailPaneView but the live metadata
update path in SkillViewApp.UpdateMetadataPane bypassed it by directly
setting _metadataPane.Text.

- Add SetMetadataContent(string?) to SkillDetailPaneView: updates
  MetadataPane.Text and auto-sizes MetadataFrame.Height. MaxMetadataHeight
  constant moves here from SkillViewApp.
- Refactor SetMetadataChips to delegate to SetMetadataContent so both
  callers share the same text/sizing path.
- Wire UpdateMetadataPane to call _discoverTab.DetailPane.SetMetadataContent
  instead of reaching into _metadataPane directly.
- Remove the now-redundant _metadataPane private field, its assignment,
  and the duplicate MinMetadataHeight / MaxMetadataHeight constants from
  SkillViewApp.
- Add five focused unit tests covering both helpers and the null/empty
  reset path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add InstalledInventoryFormatter with DescribeLocation and DescribeAgents
  pure formatting helpers (no UI dependencies)
- Replace InstalledTabView's Scope column with Location column using
  InstalledInventoryFormatter.DescribeLocation
- Use InstalledInventoryFormatter.DescribeAgents for the Agents column
- Update footer to use 'location(s)' inventory-first wording instead of 'root(s)'
- Remove now-unused DisplayScope private method from InstalledTabView
- Add InstalledInventoryFormatterTests covering all three scopes, empty agents,
  single agent, and duplicate-agent deduplication

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Installed tab column was renamed to 'Location' in Task 4, but the
internal SortMode enum and sort label still used 'Scope'. This aligns
the sort state with the visible column name.

Changes:
- SortMode.Scope → SortMode.Location
- Footer sort label 'scope' → 'location'
- Sort cycle transitions updated to use SortMode.Location

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ChangesQueueRow record and ChangesQueueBuilder pure static builder
  that concatenates Update → Cleanup → Diagnostics rows in priority order.
- Upgrade ChangesTabView from skeleton to real maintenance-queue view:
  full-width table, Load() binding, LoadAsync() that populates from
  snapshot (skills as update candidates, CleanupClassifier for cleanup
  candidates, fixed Diagnostics row), Enter key drills into the
  appropriate specialist flow.
- Wire ChangesTabView into SkillViewApp: ActivateTab(Changes) now shows
  the queue view, OpenUpdatesFromChanges() hands off to UpdatesTabView,
  Cleanup row delegates to ShowCleanupScreen(), Diagnostics row enters
  Doctor.
- Add 6 ChangesQueueBuilderTests covering ordering, empty inputs, and
  single-kind cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename ChangesQueueBuilder.Build → BuildForTests (spec-required API name)
- Move ChangesQueueBuilder.cs from Ui/Tabs/ to Ui/ (spec-required location),
  updating namespace SkillView.Ui.Tabs → SkillView.Ui
- Add UpdatesTabView.ActivateFromChanges(Action hideChanges) so the
  Changes→Updates drill-in lives on the specialist view, not in SkillViewApp
- Add DoctorTabView.ActivateFromChanges(Action hideChanges, Action enterDoctor)
  so the Changes→Doctor drill-in is wired through the spec-named file
- Add SkillViewWorkflowCoordinator.OpenUpdatesFromChanges(ChangesTabView,
  UpdatesTabView) to own the Changes queue coordination in the coordinator layer
- Update SkillViewApp.cs: remove OpenUpdatesFromChanges(), delegate to the
  above three surfaces; keep the ChangesTabView construction and tab lifecycle
  here as minimal call-site wiring
- Update ChangesTabView and test to match new namespace and method name

All 14 ChangesQueueBuilderTests + UpdatesTabViewTests + DoctorScreenTests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ate update row

- ChangesTabView.cs: GetOrCreateColumnStyle(0) now targets the Kind column
  (was incorrectly targeting column 1, the Title column)
- ChangesTabView.LoadAsync: replace per-skill Update rows with a single
  aggregate row ('N skill(s) installed — check for updates') so the queue
  does not overstate update work when availability is unknown
- SkillViewWorkflowCoordinator.OpenUpdatesFromChanges: replace concrete
  ChangesTabView/UpdatesTabView parameters with (Action hideChanges,
  Action<Action> activateUpdates) callbacks, removing concrete UI coupling;
  drop the now-unused SkillView.Ui.Tabs import
- SkillViewApp.cs: update call-site to pass the two callback lambdas

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
harder and others added 13 commits May 20, 2026 17:25
Issue 1 — Changes queue overstates pending work:
- Remove the unconditional "N skill(s) installed — check for updates"
  aggregate row from LoadAsync. Update availability is unknown until a
  dry-run runs; surfacing a fabricated row misleads the user about the
  queue's actual size.
- Remove the unconditional "Run Doctor" diagnostics row. Doctor is
  always reachable via 'd' from any screen; no need to inject it as
  pending work.
- Only Cleanup items from CleanupClassifier (real filesystem findings)
  remain in the queue.

Issue 2 — Updates flow returns to Discover instead of Changes:
- Add _openedUpdatesFromChanges flag set in onActivateUpdates.
- Replace the inline () => ActivateTab(Discover) with a new
  LeaveUpdates() method that mirrors LeaveDoctor's pattern:
  when the flag is set, hide UpdatesTabView and drive ActivateTab
  back to Changes (bypassing the no-op guard via a temporary
  _activeTab reset); otherwise fall back to Discover.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DescribeWorkspaceSummary was calling SkillHealthFormatter.RowBadge and
comparing its string output to determine behavior. This coupled behavior
to human-facing display strings that could change without warning.

Replace the string comparisons with direct checks on ValidityState and
IsSymlinked so the formatter remains a pure presentation layer and
DescribeWorkspaceSummary is driven by data.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
InstalledTabView replaced the narrow '!' and 'Lnk' indicators (totaling ~4 chars)
with a wider 'Health' column (MinWidth=10). Update the fixed-column budget in
RecomputeColumnWidths from the legacy '6 + 1 + 3' magic number to explicit
named constants that account for the actual column configuration:

- LocationColumnWidth = 6 (Location column, no explicit sizing)
- HealthColumnMinWidth = 10 (from BuildTableSource Health column)

This ensures the width allocation math doesn't drift when columns are modified,
and leaves more accurate budget space for Name/Package/Agents allocation under
tighter terminal widths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….2.3

Pick up recent upstream hang-related fixes by upgrading from Terminal.Gui
2.2.1 to 2.2.2-develop.18 (the latest published develop build on NuGet).

Also upgrade Terminal.Gui.Editor from 2.2.1 to stable 2.2.3, which requires
Terminal.Gui >= 2.2.1 and is compatible with the develop build.

Update all version-facing references in:
- SkillView.Core.csproj (package pins)
- PackageReferenceTests.cs (expected version assertion)
- CliDispatcherHelpTests.cs (version output check - updated to match assembly
  version normalization behavior)
- README.md, AGENTS.md (documentation)
- SkillViewApp.cs (code comment)

Validated with:
- dotnet restore
- dotnet test (targeted filter covering 22 tests including PackageReference
  and CliDispatcherHelp tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update stale version-facing statements to reflect the current Terminal.Gui
pin of 2.2.2-develop.18:

- Line 100: Update AOT compatibility note to reference 2.2.2-develop.18
- Line 204: Update upstream dependencies note to reference 2.2.2-develop.18
- Remove stale reference to 2.1.1-develop.* AOT configuration-crash fix

The changes align with the actual TerminalGuiVersion property in
SkillView.Core.csproj which defaults to 2.2.2-develop.18.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…guard

This addresses two narrow review issues:

1. InstalledTabView LocationColumnWidth hardcode:
   - Changed LocationColumnWidth from 6 to 7 to account for 'Project' (7 chars)
   - Added comment documenting the derivation from InstalledInventoryFormatter
     actual values: 'User' (4), 'Project' (7), 'Custom' (6)
   - Fixes understated width budget on tighter terminal widths

2. Terminal.Gui.Editor pairing guard and justification:
   - Added SkillViewCore_PairsEditorWithBaseVersion test to catch future
     version drift between Terminal.Gui and Terminal.Gui.Editor
   - Updated AGENTS.md to document the intentional pairing of develop
     Terminal.Gui (2.2.2-develop.18) with stable Terminal.Gui.Editor (2.2.3)
   - Explains that develop build addresses prior hangs while stable editor
     remains forward-compatible

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace old _statusLabel and stock StatusBar controls with the planned
ContextBarView + StatusStripView components. The new status strip now
dynamically updates hints based on active workspace (Discover/Installed/
Changes) and log mode. Context bar is wired and ready for facet display.

Also update all user-facing guidance from obsolete 'Search / Installed /
Updates' terminology to the current 'Discover / Installed / Changes'
workspace model. This applies to HelpOverlay, TuiHelpers hints, and README.

Fixes:
- Context bar positioned at Y=2 below tab strip
- Status strip at bottom replaces old label + StatusBar pair
- All tabs now start at Y=2 to accommodate context bar
- Status hints switch per workspace and log/preview mode
- Terminology updated: Search→Discover, Updates→Changes throughout
- StatusTextForTests tracks _currentStatus for test compatibility

All UI tests pass (237 total). Chrome implementation now matches approved
UX spec §44-49, §114-140.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… filters

- Add UpdateContextBar() to populate ContextBarState with current workspace,
  agent filter, and hidden-dirs state
- Call UpdateContextBar() from BuildUi(), ActivateTab(), RefreshHiddenDirUi(),
  and SubmitSearch() to keep the bar live as state changes
- Render Workspace field in ContextBarView.FormatForTests() so the bar shows
  the active tab (Discover/Installed/Changes)
- Show agent filter and hidden-dirs state on Discover tab
- Add ContextBarForTests property to expose the view for testing
- Add focused tests to verify context bar is created and responds to state changes

The context bar now surfaces real, live state instead of staying blank.
This fixes the dead code issue from the workflow-first-ux design.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Agent field now suppresses global shortcuts during text entry
   - Added _agentField check to OnWindowShortcut guard
   - Prevents letters like 'd', 'u', 'i' from triggering app actions while typing

2. Changes summary no longer contradicts empty queue state
   - Fixed DescribeWorkspaceSummary to only show health concerns when cleanup items exist
   - Prevents 'Needs review' status with 'no pending changes' text

3. Updated docs/help to match actual Changes workspace behavior
   - Removed references to row marking, batch update, and 60/40 layout
   - Changes tab now correctly described as maintenance queue with Enter to open

4. Context bar now meaningful for Installed/Changes workspaces
   - Installed shows filter text and pin filter state
   - Changes shows pending item count
   - Added state change callbacks to both tab views

5. Fixed Installed column width budgeting
   - Health column now allocates 12 chars for 'Needs review' (was 10)
   - Width calculations derive from actual possible values
   - MinWidth also updated to 12 to match allocation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes four remaining issues from the final review:

1. Changes empty-state messaging: Updated to be honest about what the
   Changes tab tracks. Changed 'Healthy' to 'No cleanup items' and
   removed the overly optimistic 'everything looks good' text. Added
   clarifying comment that update availability is unknown until dry-run.

2. Changes shortcut guidance: Fixed status strip to show 'u: Changes'
   instead of incorrect 'u: Updates', matching the actual global
   key mapping.

3. Shell chrome now more fully live: Implemented live context bar fields
   (LocationLabel, ProvenanceLabel, HealthLabel) for Installed tab
   selections, plus left-side facet badges showing agent icons. Added
   GetSelectedSkill() to InstalledTabView and DescribeProvenance() to
   InstalledInventoryFormatter to support this.

4. README alignment: Updated stale references to match current
   workflow-first model:
   - 'discovered roots' -> 'local inventory'
   - Sort modes: 'name/package/scope' -> 'name/package/location'
   - Tab references: 'UpdatesTabView' -> 'ChangesTabView'
   - Architecture description now matches actual tab structure

All existing tests pass (462 unit + 1 integration).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@harder
harder merged commit 5f90fce into main May 24, 2026
5 of 6 checks passed
@harder
harder deleted the winget-tui-redesign branch May 24, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant