Skip to content

Plugins settings: Enabled/Disabled tabs (drop plugin uninstall) - #42

Open
csmashe wants to merge 2 commits into
linuxfrom
plugin-uninstall-hardening
Open

Plugins settings: Enabled/Disabled tabs (drop plugin uninstall)#42
csmashe wants to merge 2 commits into
linuxfrom
plugin-uninstall-hardening

Conversation

@csmashe

@csmashe csmashe commented Jul 6, 2026

Copy link
Copy Markdown
Owner

What & why

The Plugins settings section listed every loaded plugin under one "Installed" header. With ~30 bundled plugins, you scroll past disabled ones to manage the few you actually use. This splits the list into two tabs — Enabled and Disabled — so each view only shows what belongs there. Dropdowns already show enabled-only, so no other behavior changes.

This supersedes an earlier plugin-uninstall approach (the branch's original focus). Uninstall doesn't earn its keep on Linux: it can't reclaim the /opt master (package-managed), the per-plugin user copy is tiny, and the existing Disable toggle already removes a plugin from every dropdown and all active use — reversibly, keeping settings. So uninstall was dropped and none of that work is in this PR.

Changes

  • View-model (PluginsSectionViewModel): follows the existing DictionarySection in-app tab pattern — SelectedTab + SetTabCommand + IsEnabledTabSelected/IsDisabledTabSelected flags. Refresh() partitions rows by activation state into EnabledGroups/DisabledGroups; adds per-tab counts, tab labels, and empty-state flags. Toggling a plugin moves its row between tabs automatically (existing PluginStateChangedRefresh).
  • View (PluginsSection.axaml): replaced the "Installed" header with two BoolBrushConverter-styled tab buttons; the group/row template is extracted to a shared resource and rendered by two ItemsControls (visibility bound to the tab flags), each with a per-tab empty state.
  • Localization: retired the unused Plugins.Installed; added Plugins.TabEnabled, Plugins.TabDisabled, Plugins.NoEnabledPlugins, Plugins.NoDisabledPlugins with real translations across en/de/es/ru.
  • Tests: TestPluginManagerFactory can now seed activated plugins; new PluginsSectionViewModelTests covers the partition, counts, empty-state flags, and SetTab.

Testing

  • dotnet build TypeWhisper.slnx — 0 warnings / 0 errors
  • dotnet test — Linux.Tests 620/620, Core.Tests 362/362 green
  • ReSharper (whole-solution, HINT) — 0 issues
  • Codex adversarial + standard review — no findings

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added enabled/disabled plugin tabs with separate counts, improved empty-state messaging, and clearer plugin summary presentation.
  • Localization
    • Updated plugin UI strings across multiple languages, including new tab labels and refined failure/empty-state wording.
  • Bug Fixes
    • Preserves the currently expanded plugin across refreshes and maintains correct row grouping when switching tabs.
  • Tests
    • Added/extended automated coverage for enabled/disabled partitioning, tab switching, and empty-state behavior.

Replace the single "Installed" plugin list with Enabled and Disabled
tabs, following the DictionarySection in-app tab pattern (SelectedTab +
SetTabCommand + IsXTabSelected flags styled via BoolBrushConverter; no
TabControl).

Refresh() now partitions the plugin rows by activation state into
EnabledGroups / DisabledGroups, with per-tab counts, tab labels, and
empty states. Dropdowns already list enabled plugins only, so no other
behavior changes.

Retire the now-unused Plugins.Installed string and add
Plugins.Tab{Enabled,Disabled} and Plugins.No{Enabled,Disabled}Plugins
with real translations across en/de/es/ru.

Tests: seed activated plugins via TestPluginManagerFactory and add
PluginsSectionViewModelTests covering the partition, counts, empty-state
flags, and SetTab.

This supersedes the earlier (never-committed) plugin uninstall approach,
which could not reclaim the package-managed /opt master and duplicated
what the existing Disable toggle already does.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 788bbe15-7df0-44ab-a934-33262617c129

📥 Commits

Reviewing files that changed from the base of the PR and between a3f64b2 and acb3488.

📒 Files selected for processing (2)
  • src/TypeWhisper.Linux/Resources/Localization/de.json
  • src/TypeWhisper.Linux/Resources/Localization/es.json
✅ Files skipped from review due to trivial changes (1)
  • src/TypeWhisper.Linux/Resources/Localization/de.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/TypeWhisper.Linux/Resources/Localization/es.json

📝 Walkthrough

Walkthrough

The Plugins section now separates plugins into enabled and disabled tabs, with updated view model state, UI rendering, localization strings, and test coverage for partitioning and tab selection.

Changes

Plugins Enabled/Disabled Tabs

Layer / File(s) Summary
ViewModel partitioning and tab state
src/TypeWhisper.Linux/ViewModels/Sections/PluginsSectionViewModel.cs
Replaces single PluginGroups with EnabledGroups/DisabledGroups, adds SelectedTab, AllRows, BuildGroups(...), SetTab(...), and updates Refresh()/ToggleExpandedAsync to use partitioned collections.
Tab UI and shared category template
src/TypeWhisper.Linux/Views/Sections/PluginsSection.axaml
Adds xmlns:local, introduces reusable PluginCategoryGroupTemplate, replaces tab header with SetTabCommand-bound buttons, and renders EnabledGroups/DisabledGroups with empty-state text.
Localization strings for tabs and states
src/TypeWhisper.Linux/Resources/Localization/de.json, en.json, es.json, ru.json
Adds and updates Plugins.* strings for empty enabled/disabled states, enabled/disabled tab labels, and plugin summary wording across all four locales.
Tests and test infrastructure
tests/TypeWhisper.Linux.Tests/PluginsSectionViewModelTests.cs, TestPluginManagerFactory.cs, PluginCollectionSettingsViewModelTests.cs
Adds new PluginsSectionViewModelTests covering partitioning, counts, and tab toggling; extends TestPluginManagerFactory.Create with activatedPluginIds; updates existing test to search across EnabledGroups/DisabledGroups.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant View as PluginsSection.axaml
  participant VM as PluginsSectionViewModel
  participant Groups as EnabledGroups/DisabledGroups

  User->>View: Click tab button
  View->>VM: SetTabCommand(0 or 1)
  VM->>VM: SelectedTab = parameter
  VM->>VM: Notify IsEnabledTabSelected/IsDisabledTabSelected
  VM->>VM: Refresh()
  VM->>VM: Partition plugins by activation state
  VM->>Groups: BuildGroups(enabled/disabled by CategoryKey)
  VM->>View: Update EnabledGroups/DisabledGroups, counts, labels
  View->>User: Render selected tab content
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: splitting Plugins settings into enabled/disabled tabs and dropping uninstall work.
Description check ✅ Passed The description covers the change summary and testing, though it doesn't use the exact template headings and omits Related Issue/Notes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch plugin-uninstall-hardening

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/TypeWhisper.Linux/ViewModels/Sections/PluginsSectionViewModel.cs (1)

185-189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication: enabledCount recomputed separately from EnabledCount.

EnabledCount (Line 126) already derives from EnabledGroups.Sum(...), but HeaderSummary recomputes an equivalent value via plugins.Count(p => p.IsEnabled) at Line 207. Both should stay in sync since BuildGroups runs first, but consider reusing EnabledCount for clarity and to avoid maintaining two equivalent computations.

♻️ Suggested simplification
-        var enabledCount = plugins.Count(p => p.IsEnabled);
-        HeaderSummary = Loc.Instance.GetString("Plugins.HeaderSummary", plugins.Count, enabledCount);
+        HeaderSummary = Loc.Instance.GetString("Plugins.HeaderSummary", plugins.Count, EnabledCount);

Also applies to: 207-208

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TypeWhisper.Linux/ViewModels/Sections/PluginsSectionViewModel.cs` around
lines 185 - 189, `HeaderSummary` in `PluginsSectionViewModel` is recomputing the
enabled plugin count with `plugins.Count(p => p.IsEnabled)` even though
`EnabledCount` already exposes the same value from `EnabledGroups`. Update the
`HeaderSummary` logic to reuse `EnabledCount` instead of duplicating the count
calculation, and keep the summary text generation in sync with the existing
`EnabledGroups`/`DisabledGroups` state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/TypeWhisper.Linux/Resources/Localization/de.json`:
- Line 438: The German localization string for Plugins.SummaryFailed is
singular-only even though it is rendered with a count. Update the de.json entry
used by the Plugins.SummaryFailed resource to use a plural-neutral wording, or
split it into separate singular and plural variants if that pattern is already
used elsewhere in the localization set. Keep the change localized to the
resource key so all count-based renderings of the summary read correctly.

---

Nitpick comments:
In `@src/TypeWhisper.Linux/ViewModels/Sections/PluginsSectionViewModel.cs`:
- Around line 185-189: `HeaderSummary` in `PluginsSectionViewModel` is
recomputing the enabled plugin count with `plugins.Count(p => p.IsEnabled)` even
though `EnabledCount` already exposes the same value from `EnabledGroups`.
Update the `HeaderSummary` logic to reuse `EnabledCount` instead of duplicating
the count calculation, and keep the summary text generation in sync with the
existing `EnabledGroups`/`DisabledGroups` state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f65c4e5-4f44-448a-936a-1eb6ac3c3397

📥 Commits

Reviewing files that changed from the base of the PR and between e189d7f and a3f64b2.

📒 Files selected for processing (9)
  • src/TypeWhisper.Linux/Resources/Localization/de.json
  • src/TypeWhisper.Linux/Resources/Localization/en.json
  • src/TypeWhisper.Linux/Resources/Localization/es.json
  • src/TypeWhisper.Linux/Resources/Localization/ru.json
  • src/TypeWhisper.Linux/ViewModels/Sections/PluginsSectionViewModel.cs
  • src/TypeWhisper.Linux/Views/Sections/PluginsSection.axaml
  • tests/TypeWhisper.Linux.Tests/PluginCollectionSettingsViewModelTests.cs
  • tests/TypeWhisper.Linux.Tests/PluginsSectionViewModelTests.cs
  • tests/TypeWhisper.Linux.Tests/TestPluginManagerFactory.cs

Comment thread src/TypeWhisper.Linux/Resources/Localization/de.json Outdated
Plugins.SummaryFailed is rendered with a numeric count, but the German
string ("konnte ... geladen") was singular-only and the Spanish
("no se pudieron cargar") plural-only, so one of count == 1 / count > 1
always read ungrammatically. Switch both to count-neutral phrasings
("{0} nicht geladen" / "{0} sin cargar"). en and ru were already
count-neutral. (CodeRabbit PR #42.)
Comment on lines +237 to +241
foreach (var group in rows.GroupBy(p => p.CategoryKey))
{
var categoryPlugins = group.ToList();
target.Add(new PluginCategoryGroup(categoryPlugins[0].CategoryLabel, categoryPlugins));
}
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.

2 participants