Skip to content

feat(plugins): redesign plugins view to match mockup#80

Merged
mpiton merged 6 commits intomainfrom
feat/plugins-view-redesign
Apr 20, 2026
Merged

feat(plugins): redesign plugins view to match mockup#80
mpiton merged 6 commits intomainfrom
feat/plugins-view-redesign

Conversation

@mpiton
Copy link
Copy Markdown
Owner

@mpiton mpiton commented Apr 19, 2026

Summary

Refactor the plugins catalogue page to match the mockup design while keeping every existing Tauri IPC command behind it unchanged.

  • New PluginsHeader — title, enabled/disabled counters, "Check updates" action
  • New PluginsToolbar — segmented category pills replacing the Select dropdown, inline search
  • Category-grouped sections (uppercase labels, rounded card containers)
  • Monogram icons (accent-tinted for crawlers/extractors)
  • Installed plugins → Switch toggle (calls plugin_disable)
  • Update available → inline "↑ vX.Y.Z" pill next to the toggle
  • Not installed → single Install button
  • Destructive Uninstall action moved to a kebab menu (reduces noise)
  • i18n keys added for stats, categories, actions (fr + en)
  • Tests: 11 PluginStoreRow + 4 PluginsHeader + 4 PluginsToolbar + 3 groupByCategory
  • issue30-ui-fr updated to match the new French copy

Test plan

  • npx vitest run — 389/389 green
  • npx oxlint src/views/PluginsView src/views/PluginsView.tsx src/i18n — clean
  • npx tsc --noEmit — clean
  • Manual: open /plugins, confirm header / grouped sections render
  • Manual: toggle off an installed plugin → triggers plugin_disable
  • Manual: click Install on a not-installed plugin → catalogue row refreshes
  • Manual: open kebab menu → Uninstall triggers plugin_uninstall
  • Manual: type in the search field → list filters live
  • Manual: switch locale to fr → every label translated

Summary by cubic

Redesigned the Plugins catalog to match the mockup, with a compact header, segmented filter, grouped sections, and streamlined row actions. Adds an optimistic Disable/Enable flow that dims rows and updates the enabled counter; IPC mutations remain unchanged.

  • New Features

    • Header with enabled counter and “Check updates”; counter excludes locally disabled plugins.
    • Toolbar with category pills and localized search; placeholder uses an ellipsis.
    • Grouped sections per category with uppercase labels and counts; category labels translated with a slug fallback.
    • Monogram icons (accent tint for crawler/extractor).
    • Row actions: single Install for not-installed; inline update pill prefixed with “v”; installed version label; kebab menu with Disable/Enable and Uninstall; “inactive” badge + dimmed row when locally disabled.
    • i18n in en/fr: title, stats, categories, actions (incl. Enable), search, empty/error/loading/retry, badges (“official”, “inactive”), toasts (incl. enable success); pluralization uses _one/_other.
    • Tests added for PluginStoreRow, PluginsHeader, PluginsToolbar, and groupByCategory.
  • Bug Fixes

    • Clearing locally disabled state on uninstall success to prevent a reinstalled plugin from appearing inactive until reload.

Written for commit 891a113. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Redesigned Plugins UI: header with enabled counter & refresh, toolbar with category tabs and search, grouped plugin sections, compact rows, and bottom spacer
  • UX

    • Contextual Install/Update buttons and a “More” menu exposing Disable/Enable and Uninstall actions; installed version and official/inactive indicators; optimistic local disable state
  • Localization

    • Added comprehensive English and French plugins strings, including enable success toast
  • Tests

    • New unit/UI tests for header, toolbar, rows, grouping, and behaviors
  • Documentation

    • Clarified changelog wording about download completion event timing (emitted after persistence)

Refactor the plugins catalogue page to align with the mockup design:
- PluginsHeader with enabled/disabled counters and "Check updates" action
- PluginsToolbar with segmented category pills replacing the Select
- Category-grouped sections with uppercase labels and card containers
- Monogram icons (accent-tinted for crawlers/extractors)
- Switch toggle for installed plugins (calls plugin_disable)
- Inline update pill for plugins with an available update
- Kebab menu hosting the destructive "Uninstall" action

Underlying mutations (install, update, disable, uninstall, refresh)
are unchanged; the refactor is UI-only. i18n keys added for stats,
categories and actions in both fr and en.
@github-actions github-actions Bot added documentation Improvements or additions to documentation frontend labels Apr 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a plugins UI: header, toolbar, grouped plugin listing with search/category filtering, redesigned plugin rows, i18n strings in English and French, grouping utility, tests, and a minor changelog wording edit about download completion event timing.

Changes

Cohort / File(s) Summary
Changelog & Locales
CHANGELOG.md, src/i18n/locales/en.json, src/i18n/locales/fr.json
Edited changelog wording for download completion event timing; added comprehensive plugins i18n keys in English and French (including plugins.toast.enableSuccess).
PluginsView Core
src/views/PluginsView.tsx
Refactored to use PluginsHeader and PluginsToolbar; added memoized search + category filtering, optimistic local disable state, enable/disable mutations, grouping via groupByCategory, grouped rendering, i18n-driven states, and wiring of row handlers.
Plugin Row
src/views/PluginsView/PluginStoreRow.tsx
Redesigned row (monogram icon, compact metadata, badges), integrated i18n and lucide icons, conditional install/update actions, and a “more” kebab dropdown for enable/disable/uninstall; added isLocallyDisabled and onEnable props.
New UI Components
src/views/PluginsView/PluginsHeader.tsx, src/views/PluginsView/PluginsToolbar.tsx
Added PluginsHeader (title, enabled count, refresh) and PluginsToolbar (category tabs with ARIA roles and search input) with accessible semantics and i18n.
Grouping Utility
src/views/PluginsView/groupByCategory.ts
Added PluginGroup interface and groupByCategory(entries) that buckets entries by category while preserving insertion order.
Tests
src/views/PluginsView/__tests__/*, src/i18n/__tests__/issue30-ui-fr.test.tsx
Added tests for PluginStoreRow, PluginsHeader, PluginsToolbar, and groupByCategory; adjusted an existing French UI i18n test and test helper formatting.
Misc
src/i18n/__tests__/issue30-ui-fr.test.tsx
Minor test helper and assertion formatting adjustments; updated French placeholder to use U+2026 ellipsis character.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Toolbar as "PluginsToolbar\n(rgba(70,130,180,0.5))"
    participant View as "PluginsView\n(rgba(34,139,34,0.5))"
    participant Header as "PluginsHeader\n(rgba(255,165,0,0.5))"
    participant Row as "PluginStoreRow\n(rgba(147,112,219,0.5))"
    participant API as "Backend / Mutations\n(rgba(220,20,60,0.5))"

    User->>Toolbar: select category / type search
    Toolbar->>View: onCategoryChange / onSearchChange
    View->>View: filter entries (memoized)
    View->>View: groupByCategory (memoized)
    View->>Header: update enabledCount
    View->>Row: render grouped rows
    User->>Row: click install/update/uninstall/disable/enable
    Row->>API: call mutation (install/update/disable/enable/uninstall)
    API-->>Row: mutation result
    Row->>View: callbacks / invalidate queries
    View->>Header: refresh counts / rerender
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

ui

Poem

🐰 I hop through keys and tiny strings,
Tabs and search and grouped-up things,
A header twinkles, rows align,
Buttons click and mutations whine,
I nibble tests and call it fine.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(plugins): redesign plugins view to match mockup' accurately summarizes the primary change—a complete redesign of the plugins view UI.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/plugins-view-redesign

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 19, 2026

Greptile Summary

This PR replaces the flat plugins catalogue with a grouped, mockup-aligned layout: a compact PluginsHeader (enabled counter + refresh), a segmented PluginsToolbar (category pills + search), and category-grouped rows with monogram icons and a kebab menu for Disable/Enable/Uninstall. An optimistic locallyDisabled session-state bridges the gap until PluginStoreEntryDto gains a "disabled" status variant, and all existing Tauri IPC commands remain unchanged. The two previously flagged P1 issues (hardcoded disabledCount and always-checked Switch) are fully resolved in this revision.

Confidence Score: 5/5

Safe to merge — all prior P1 findings are resolved; remaining items are minor P2 suggestions.

Both previously flagged P1 issues are addressed: disabledCount is removed from the header and the always-checked Switch is replaced with a proper kebab menu. The only remaining findings are P2: two unused i18n keys and incomplete ARIA tab semantics. Neither blocks correct runtime behaviour.

No files require special attention beyond the two P2 suggestions in PluginsToolbar.tsx and en.json/fr.json.

Important Files Changed

Filename Overview
src/views/PluginsView.tsx Main view orchestrates header, toolbar, and grouped rows; optimistic locallyDisabled state and enabledCount derivation are correct; previous hardcoded-0 and Switch issues are resolved.
src/views/PluginsView/PluginStoreRow.tsx Row renders monogram icon, conditional install/update/kebab actions; separator logic is correct; Switch replaced with a proper Disable/Enable/Uninstall kebab menu.
src/views/PluginsView/PluginsHeader.tsx Compact header with enabled counter and refresh button; disabledCount prop removed since the DTO has no disabled status variant.
src/views/PluginsView/PluginsToolbar.tsx Category pills + search input; uses role="tablist" / role="tab" / aria-selected without aria-controls or corresponding role="tabpanel" — the ARIA tab relationship is incomplete.
src/views/PluginsView/groupByCategory.ts Simple Map-based grouping that preserves insertion order; well-tested by the accompanying test file.
src/i18n/locales/en.json New plugins.* keys added; plugins.action.update and plugins.action.browseRepo are defined but never consumed by any component.
src/i18n/locales/fr.json French translations for all new plugin keys match their English counterparts; pluralization is correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    PV[PluginsView]
    PV --> PH[PluginsHeader\nenabled count + refresh]
    PV --> PT[PluginsToolbar\ncategory pills + search]
    PV --> GBC[groupByCategory\nfiltered entries]
    GBC --> SEC[section per category]
    SEC --> PSR[PluginStoreRow × N]

    PSR -->|status = not_installed| INS[Install button\n→ plugin_store_install]
    PSR -->|status = update_available| UPD[Version pill\n→ plugin_store_update]
    PSR -->|installed-like| KEB[Kebab menu]
    KEB -->|!locallyDisabled| DIS[Disable\n→ plugin_disable\nsets locallyDisabled]
    KEB -->|locallyDisabled| ENB[Enable\n→ plugin_enable\nclears locallyDisabled]
    KEB --> UNI[Uninstall\n→ plugin_uninstall\nclears locallyDisabled]

    PH -->|onRefresh| REF[plugin_store_refresh]
Loading

Fix All in Claude Code

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/i18n/locales/en.json
Line: 261

Comment:
**Unused `update` and `browseRepo` i18n keys**

`plugins.action.update` and `plugins.action.browseRepo` are defined in both `en.json` and `fr.json` but are never referenced in any component. The update button in `PluginStoreRow` renders `v{entry.version}` directly without a translated label, and no "Browse repository" action is wired up. These dead keys will silently diverge from reality if translators later update them.

Consider removing both keys until a component actually consumes them, or add a comment explaining they're intentionally reserved for upcoming work.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/views/PluginsView/PluginsToolbar.tsx
Line: 23-46

Comment:
**Incomplete ARIA tab semantics**

The container has `role="tablist"` and each pill has `role="tab"` + `aria-selected`, but no `aria-controls` attribute links a tab to its panel, and the content sections in `PluginsView` have no matching `role="tabpanel"` / `id`. Screen readers will announce these as tabs that control a panel, but cannot navigate to that panel, leaving keyboard-only users stranded.

Either complete the pattern by assigning stable `id`s to the section panels and wiring up `aria-controls`, or replace `role="tablist"` / `role="tab"` with `role="toolbar"` / `role="button"` to match what the widget actually does (a filter control, not a tabbed interface).

How can I resolve this? If you propose a fix, please make it concise.

Reviews (6): Last reviewed commit: "fix(plugins): clear locallyDisabled on u..." | Re-trigger Greptile

Comment thread src/views/PluginsView.tsx Outdated
Comment thread src/views/PluginsView/PluginStoreRow.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

10-50: ⚠️ Potential issue | 🔴 Critical

Duplicate ### Added section heading breaks changelog format.

The changelog contains two ### Added sections under ## [Unreleased] (lines 10 and 49), which violates the Keep a Changelog format. Each section type should appear only once per version.

Merge these into a single ### Added section by moving the useTauriMutation entry (lines 12-13) into the larger ### Added section starting at line 49, then remove the first section heading and its surrounding blank lines.

📝 Proposed fix to merge the duplicate sections
 ## [Unreleased]
 
-### Added
-
-- `useTauriMutation` now accepts `silentError` (opt-out of the default toast) and `errorMessage` (remap the error message before toasting) options. (`#74`)
-
 ### Changed
 
 - Plugins view refreshed to match the design mockup: a header with enabled/disabled counters and a "Check updates" action, a segmented category filter replacing the dropdown, grouped sections per category with uppercase labels, monogram icons with accent coloring for crawlers/extractors, a toggle for installed plugins, and a kebab menu hosting the destructive "Uninstall" action. Installable plugins keep a single `Install` button; pending updates surface as an inline pill on the row.

Then move the useTauriMutation bullet to the beginning of the ### Added section at line 49:

 ### Added
 
+- `useTauriMutation` now accepts `silentError` (opt-out of the default toast) and `errorMessage` (remap the error message before toasting) options. (`#74`)
 - **Clear completed / Clear failed downloads**: two new toolbar buttons in the Downloads view, separated from the bulk actions by a vertical separator. Each opens a confirmation dialog with an optional "Also delete files from disk" checkbox gated behind a prominent red warning panel. Success and error outcomes are reported via toasts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 10 - 50, There are two duplicate "### Added"
headings; remove the first "### Added" heading block (including its surrounding
blank lines) and move the `- useTauriMutation` bullet (the line mentioning
`silentError` and `errorMessage`) into the existing larger "### Added" section
later in the file so there is only one "### Added" section; ensure the moved
bullet is placed at the start of that single "### Added" section and delete the
now-empty original heading.
🧹 Nitpick comments (1)
src/views/PluginsView/PluginsToolbar.tsx (1)

23-46: Consider whether full tab semantics are appropriate here.

The role="tablist" and role="tab" pattern typically implies tab panels that get revealed/hidden. Here it's used for filtering, which is more of a toggle button group. This isn't strictly incorrect but may confuse assistive technology users expecting panel switching.

If keeping tabs, consider adding aria-controls pointing to the content area. Alternatively, a radio group or toggle button pattern could be more semantically accurate for filter controls.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/PluginsView/PluginsToolbar.tsx` around lines 23 - 46, The toolbar
currently uses role="tablist" and role="tab" in PluginsToolbar (the
categories.map buttons with onCategoryChange, categories, activeCategory and
label variables), which implies tab panels; either add proper tab semantics by
giving each button an aria-controls that references the ID of the associated
content panel and ensure the panel has a matching id and appropriate aria-hidden
toggling, or change the pattern to a more accurate filter control (e.g.,
role="radiogroup" on the container and role="radio" with aria-checked on each
button or plain buttons with aria-pressed) and update the buttons' attributes
(aria-selected → aria-checked/aria-pressed) to match that choice so assistive
tech will interpret the control correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/i18n/locales/en.json`:
- Around line 250-255: The stats plural keys use the outdated `_plural` suffix;
update the keys under the "stats" object by replacing "enabled_plural" and
"disabled_plural" with i18next-standard "enabled_one"/"enabled_other" and
"disabled_one"/"disabled_other" respectively (leave the existing "enabled" and
"disabled" entries as appropriate), so the i18n pluralization for the stats
strings works with functions that pass a count.

In `@src/i18n/locales/fr.json`:
- Around line 250-254: The plural keys under "stats" (stats.enabled,
stats.enabled_plural, stats.disabled, stats.disabled_plural) don't match the
repo's i18next plural shape and will break PluginsHeader.tsx which passes {
count }; rename the plural keys to use the existing pattern (e.g.,
stats.enabled_one and stats.enabled_other, stats.disabled_one and
stats.disabled_other) and keep the corresponding French translations so
i18next's plural resolver picks the correct form when count > 1.

In `@src/views/PluginsView.tsx`:
- Around line 70-74: The disabledCount is hardcoded to 0 in PluginsView.tsx
which makes the UI counter meaningless; update the logic so disabledCount is
computed from entries (e.g., const disabledCount = useMemo(() =>
entries.filter(e => !isInstalled(e.status)).length, [entries]) or, if the data
model doesn't support a disabled state yet, remove the disabled counter from the
header or add a TODO comment next to the disabledCount declaration to mark it as
a placeholder; locate and modify the disabledCount declaration near enabledCount
and use entries and isInstalled to drive the value.

In `@src/views/PluginsView/PluginStoreRow.tsx`:
- Around line 78-88: The update-pill currently renders entry.version without a
leading "v"; update the JSX inside the Button (the element rendered when
entry.status === "update_available") to prepend a literal "v" before
entry.version (so it renders "vX.Y.Z"), matching the installed-version label
display; keep the existing onUpdate call, disabled={isUpdating}, and visual
children (ArrowUpCircle) intact.

---

Outside diff comments:
In `@CHANGELOG.md`:
- Around line 10-50: There are two duplicate "### Added" headings; remove the
first "### Added" heading block (including its surrounding blank lines) and move
the `- useTauriMutation` bullet (the line mentioning `silentError` and
`errorMessage`) into the existing larger "### Added" section later in the file
so there is only one "### Added" section; ensure the moved bullet is placed at
the start of that single "### Added" section and delete the now-empty original
heading.

---

Nitpick comments:
In `@src/views/PluginsView/PluginsToolbar.tsx`:
- Around line 23-46: The toolbar currently uses role="tablist" and role="tab" in
PluginsToolbar (the categories.map buttons with onCategoryChange, categories,
activeCategory and label variables), which implies tab panels; either add proper
tab semantics by giving each button an aria-controls that references the ID of
the associated content panel and ensure the panel has a matching id and
appropriate aria-hidden toggling, or change the pattern to a more accurate
filter control (e.g., role="radiogroup" on the container and role="radio" with
aria-checked on each button or plain buttons with aria-pressed) and update the
buttons' attributes (aria-selected → aria-checked/aria-pressed) to match that
choice so assistive tech will interpret the control correctly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 844f2013-0f34-46ac-b7b5-b19054691ef1

📥 Commits

Reviewing files that changed from the base of the PR and between 2eed1eb and 062ddd5.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • src/i18n/__tests__/issue30-ui-fr.test.tsx
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/views/PluginsView.tsx
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/views/PluginsView/PluginsHeader.tsx
  • src/views/PluginsView/PluginsToolbar.tsx
  • src/views/PluginsView/__tests__/PluginStoreRow.test.tsx
  • src/views/PluginsView/__tests__/PluginsHeader.test.tsx
  • src/views/PluginsView/__tests__/PluginsToolbar.test.tsx
  • src/views/PluginsView/__tests__/groupByCategory.test.ts
  • src/views/PluginsView/groupByCategory.ts

Comment thread src/i18n/locales/en.json
Comment on lines +250 to +255
"stats": {
"enabled": "enabled",
"enabled_plural": "enabled",
"disabled": "disabled",
"disabled_plural": "disabled"
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how the stats keys are used in PluginsHeader
ast-grep --pattern 't("plugins.stats.$_"$$$)'
rg -n "plugins\.stats\." --type=ts --type=tsx -C2

Repository: mpiton/vortex

Length of output: 84


🏁 Script executed:

# First, locate the en.json file and verify its content
find . -name "en.json" -type f

Repository: mpiton/vortex

Length of output: 83


🏁 Script executed:

# Read the en.json file around the lines mentioned
head -300 src/i18n/locales/en.json | tail -60

Repository: mpiton/vortex

Length of output: 1660


🏁 Script executed:

# Search for usage of the stats keys in the codebase with correct syntax
rg -n "plugins\.stats" --type ts -C2

Repository: mpiton/vortex

Length of output: 828


🏁 Script executed:

# Also search for any ts files to understand the file structure
rg -l "plugins\.stats"

Repository: mpiton/vortex

Length of output: 96


🏁 Script executed:

# Read lines 140-150 to see the titleCompleted pattern
sed -n '140,150p' src/i18n/locales/en.json

Repository: mpiton/vortex

Length of output: 637


🏁 Script executed:

# Search for i18next configuration files
find . -name "*i18n*" -o -name "*i18next*" | grep -E "\.(ts|js|json)$" | head -20

Repository: mpiton/vortex

Length of output: 99


🏁 Script executed:

# Search for other count-based pluralization in the codebase to see patterns
rg -n "{ count:" src/ --type ts | head -20

Repository: mpiton/vortex

Length of output: 140


🏁 Script executed:

# Check if there are other _plural suffixes in en.json
rg -n "_plural" src/i18n/locales/en.json

Repository: mpiton/vortex

Length of output: 135


🏁 Script executed:

# Read the i18n configuration file
cat src/i18n/i18n.ts

Repository: mpiton/vortex

Length of output: 798


🏁 Script executed:

# Also check the test file to understand how pluralization is expected to work
cat src/i18n/i18n.test.ts

Repository: mpiton/vortex

Length of output: 889


Change pluralization suffixes from _plural to _one/_other to match i18next standard format.

The stats keys use _plural suffixes, but i18next (configured with default pluralization) expects _one and _other suffixes when a count parameter is passed. This pattern is already correctly used elsewhere in the file (e.g., titleCompleted_one/titleCompleted_other). The current keys won't trigger proper pluralization behavior.

Proposed fix
     "stats": {
-      "enabled": "enabled",
-      "enabled_plural": "enabled",
-      "disabled": "disabled",
-      "disabled_plural": "disabled"
+      "enabled_one": "enabled",
+      "enabled_other": "enabled",
+      "disabled_one": "disabled",
+      "disabled_other": "disabled"
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"stats": {
"enabled": "enabled",
"enabled_plural": "enabled",
"disabled": "disabled",
"disabled_plural": "disabled"
},
"stats": {
"enabled_one": "enabled",
"enabled_other": "enabled",
"disabled_one": "disabled",
"disabled_other": "disabled"
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/i18n/locales/en.json` around lines 250 - 255, The stats plural keys use
the outdated `_plural` suffix; update the keys under the "stats" object by
replacing "enabled_plural" and "disabled_plural" with i18next-standard
"enabled_one"/"enabled_other" and "disabled_one"/"disabled_other" respectively
(leave the existing "enabled" and "disabled" entries as appropriate), so the
i18n pluralization for the stats strings works with functions that pass a count.

Comment thread src/i18n/locales/fr.json Outdated
Comment on lines +250 to +254
"stats": {
"enabled": "activé",
"enabled_plural": "activés",
"disabled": "désactivé",
"disabled_plural": "désactivés"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In the current i18next pluralization format used by react-i18next, does t("plugins.stats.enabled", { count: 2 })resolveenabled_plural, or does it expect enabled_one/enabled_other style keys?

💡 Result:

In the current i18next pluralization format used by react-i18next, t("plugins.stats.enabled", { count: 2 }) expects enabled_other style keys (Intl.PluralRules suffixes like _one, _other, etc.), not enabled_plural. The modern JSON v4 format (default since i18next v21+) uses suffixes matching Intl.PluralRules: _one, _other, and language-specific forms like _few, _many, _zero. For English with count=2, it resolves plugins.stats.enabled_other. Older formats used _plural, but v4 is current. react-i18next inherits this from i18next. To use legacy formats, set compatibilityJSON: 'v3' or earlier, but current expects _one/_other style. Example for English: { "plugins": { "stats": { "enabled_one": "1 stat enabled", "enabled_other": "{{count}} stats enabled" } } } t("plugins.stats.enabled", { count: 2 }) → "2 stats enabled"

Citations:


🏁 Script executed:

# Find PluginsHeader.tsx and check the t() calls
find . -name "PluginsHeader.tsx" -type f 2>/dev/null | head -5

Repository: mpiton/vortex

Length of output: 98


🏁 Script executed:

# Find the French locale file
find . -path "*/locales/fr.json" -type f 2>/dev/null

Repository: mpiton/vortex

Length of output: 83


🏁 Script executed:

# Check if fr.json exists and examine the stats section
find . -name "fr.json" -path "*/locales/*" -type f 2>/dev/null -exec wc -l {} +

Repository: mpiton/vortex

Length of output: 87


🏁 Script executed:

# Read PluginsHeader.tsx to check the t() calls
cat -n ./src/views/PluginsView/PluginsHeader.tsx | grep -A 5 -B 5 "plugins.stats"

Repository: mpiton/vortex

Length of output: 879


🏁 Script executed:

# Read fr.json around lines 250-254 to see the stats section and context
sed -n '240,260p' ./src/i18n/locales/fr.json

Repository: mpiton/vortex

Length of output: 554


🏁 Script executed:

# Search for _one and _other patterns in fr.json to verify existing plural format
rg "_one|_other" ./src/i18n/locales/fr.json

Repository: mpiton/vortex

Length of output: 958


Use the repo's existing i18next plural key shape here.

PluginsHeader.tsx passes { count } into these keys, but the locale file consistently uses _one / _other elsewhere (e.g., selectedCount_one, activeCount_one, titleCompleted_one, etc.). The current enabled_plural / disabled_plural format is incompatible with the modern i18next pluralization resolver, so counts above 1 will render the singular label.

Suggested fix
     "stats": {
-      "enabled": "activé",
-      "enabled_plural": "activés",
-      "disabled": "désactivé",
-      "disabled_plural": "désactivés"
+      "enabled_one": "activé",
+      "enabled_other": "activés",
+      "disabled_one": "désactivé",
+      "disabled_other": "désactivés"
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"stats": {
"enabled": "activé",
"enabled_plural": "activés",
"disabled": "désactivé",
"disabled_plural": "désactivés"
"stats": {
"enabled_one": "activé",
"enabled_other": "activés",
"disabled_one": "désactivé",
"disabled_other": "désactivés"
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/i18n/locales/fr.json` around lines 250 - 254, The plural keys under
"stats" (stats.enabled, stats.enabled_plural, stats.disabled,
stats.disabled_plural) don't match the repo's i18next plural shape and will
break PluginsHeader.tsx which passes { count }; rename the plural keys to use
the existing pattern (e.g., stats.enabled_one and stats.enabled_other,
stats.disabled_one and stats.disabled_other) and keep the corresponding French
translations so i18next's plural resolver picks the correct form when count > 1.

Comment thread src/views/PluginsView.tsx Outdated
Comment thread src/views/PluginsView/PluginStoreRow.tsx Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 13 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/views/PluginsView.tsx">

<violation number="1" location="src/views/PluginsView.tsx:74">
P2: `disabledCount` is hardcoded to `0`, so the disabled plugins stat in the header is always incorrect.</violation>
</file>

<file name="src/i18n/locales/fr.json">

<violation number="1" location="src/i18n/locales/fr.json:251">
P2: Pluralization keys use `_plural` format, but the app is configured for i18next v4-style plural suffixes (`_one`/`_other`), so count-based labels may render incorrectly.</violation>
</file>

<file name="src/views/PluginsView/PluginStoreRow.tsx">

<violation number="1" location="src/views/PluginsView/PluginStoreRow.tsx:108">
P1: `<Switch checked />` is equivalent to `<Switch checked={true} />`, so the toggle is permanently "on" for every installed plugin. After the user clicks to disable, the query is invalidated and refetched, but since `PluginStoreEntry.status` has no `"disabled"` variant the entry still comes back as `"installed"` — the switch stays checked and the user gets no visual feedback. At minimum, make this conditional (e.g., `checked={entry.status !== "disabled"}`) so the UI will reflect the correct state once a disabled status is supported.</violation>
</file>

<file name="src/i18n/locales/en.json">

<violation number="1" location="src/i18n/locales/en.json:252">
P1: Pluralization keys use `_plural` suffix but i18next v21+ (JSON v4 format, the default) expects `_one`/`_other` suffixes. Passing `{ count }` to `t("plugins.stats.enabled")` will never resolve `enabled_plural` — it falls back to the base key for all counts. The rest of this codebase already uses `_one`/`_other` (e.g., `titleCompleted_one`/`titleCompleted_other`).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/views/PluginsView/PluginStoreRow.tsx Outdated
Comment thread src/i18n/locales/en.json Outdated
Comment thread src/views/PluginsView.tsx Outdated
Comment thread src/i18n/locales/fr.json Outdated
- Drop the Switch toggle on installed rows. The PluginStoreEntry DTO
  has no "disabled" variant, so the Switch was permanently checked and
  click → plugin_disable produced no visible feedback. The disable
  action now lives in the kebab menu alongside uninstall, matching
  what the backend can actually reflect today.
- Drop the hardcoded "disabled" counter from the header. Same reason:
  until the DTO exposes a disabled state, the counter was always 0.
- Prefix the update pill with "v" (e.g. "↑ v1.2.0") for consistency
  with the installed-version label.
- Fix i18next pluralization keys: _plural → _one/_other (v4 format
  used by the rest of the codebase).
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/views/PluginsView.tsx (1)

26-28: Share the installed-status predicate.

This exact status list is duplicated in src/views/PluginsView/PluginStoreRow.tsx as isInstalledLike(). The two copies already drive different UI paths here and in the row, so the next status change can desync header counts from row actions. Please extract one shared helper and reuse it in both places.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/PluginsView.tsx` around lines 26 - 28, The installed-status
predicate is duplicated between isInstalled (in PluginsView) and isInstalledLike
(in PluginStoreRow); extract a single exported helper (e.g., export function
isPluginInstalled(status: string): boolean) in a shared module (or promote one
of the existing functions to a shared export) that implements the status check
for "installed" | "update_available" | "downgrade", then replace both
isInstalled and isInstalledLike usages to call the new isPluginInstalled helper
so header counts and row actions use the same logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/views/PluginsView/PluginStoreRow.tsx`:
- Around line 109-137: The dropdown still shows a Disable action even though the
store DTO has no persistent disabled state; update PluginStoreRow to stop
rendering the disable item unless a real disabled flag exists or you implement
local optimistic state: either remove the onDisable menu branch (the conditional
rendering around DropdownMenuItem that uses onDisable and calls
onDisable(entry.name)) or change the condition to require a persistent flag on
entry (e.g., entry.disabled) and only show it when that flag is present, or
alternatively implement local/optimistic disabled state in PluginStoreRow that
flips on select and persists visually until PluginsView.tsx refetch resolves;
modify the rendering around onDisable, the onSelect handler that calls
onDisable(entry.name), and any toast/invalidations accordingly.
- Around line 68-73: The category is rendered raw in PluginStoreRow.tsx as
{entry.category}; resolve it through the i18n translator instead by calling t
with the key built from the category slug (e.g.
"plugins.categories.<entry.category>") and a safe fallback to entry.category
when the translation is missing; update the JSX in the component (where
entry.description/entry.category/entry.author are rendered) to use that t(...)
call so category labels show localized values for users.

---

Nitpick comments:
In `@src/views/PluginsView.tsx`:
- Around line 26-28: The installed-status predicate is duplicated between
isInstalled (in PluginsView) and isInstalledLike (in PluginStoreRow); extract a
single exported helper (e.g., export function isPluginInstalled(status: string):
boolean) in a shared module (or promote one of the existing functions to a
shared export) that implements the status check for "installed" |
"update_available" | "downgrade", then replace both isInstalled and
isInstalledLike usages to call the new isPluginInstalled helper so header counts
and row actions use the same logic.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f168c5ff-5c42-4505-8364-6da8e1a75737

📥 Commits

Reviewing files that changed from the base of the PR and between 062ddd5 and 7e46725.

📒 Files selected for processing (7)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/views/PluginsView.tsx
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/views/PluginsView/PluginsHeader.tsx
  • src/views/PluginsView/__tests__/PluginStoreRow.test.tsx
  • src/views/PluginsView/__tests__/PluginsHeader.test.tsx
✅ Files skipped from review due to trivial changes (2)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/views/PluginsView/tests/PluginsHeader.test.tsx
  • src/views/PluginsView/PluginsHeader.tsx

Comment thread src/views/PluginsView/PluginStoreRow.tsx
Comment thread src/views/PluginsView/PluginStoreRow.tsx Outdated
- Translate the inline category label in PluginStoreRow through
  t("plugins.categories.<slug>") so French users see "Crawlers"
  instead of the raw "crawler" slug. Falls back to the slug when no
  translation exists.
- Drop the Disable menu item and its plumbing. With the store DTO
  reporting no disabled state, clicking it was a no-op beyond the
  toast. Keep plugin_disable wiring out of the UI until the DTO
  grows a matching field. Removes the disable i18n keys
  (action.disable, toast.disableSuccess) and the onDisable prop.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/views/PluginsView.tsx (2)

26-28: Code duplication: isInstalled duplicates isInstalledLike from PluginStoreRow.

Both isInstalled here and isInstalledLike in PluginStoreRow.tsx (Lines 29-31) implement identical logic. Consider extracting this to a shared utility to avoid drift.

♻️ Suggested extraction

Create a shared utility, e.g., in src/views/PluginsView/utils.ts:

import type { PluginStoreEntry } from "@/types/plugin-store";

export function isInstalledLike(status: PluginStoreEntry["status"]): boolean {
  return status === "installed" || status === "update_available" || status === "downgrade";
}

Then import and use it in both files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/PluginsView.tsx` around lines 26 - 28, The isInstalled function in
PluginsView duplicates the logic of isInstalledLike in PluginStoreRow; extract
the shared logic into a single utility function (e.g., export function
isInstalledLike(status: PluginStoreEntry["status"]) { ... }) and replace both
the existing isInstalled in PluginsView and isInstalledLike in PluginStoreRow to
import and call that shared utility; update imports in PluginsView and
PluginStoreRow to use the new exported function and remove the duplicate
implementation.

47-50: Consider adding error handling for the uninstall mutation.

The mutation has onSuccess but no onError callback. If uninstall fails, users won't receive feedback.

♻️ Suggested fix
   const uninstallMutation = useTauriMutation<void, { name: string }>("plugin_uninstall", {
     invalidateKeys: STORE_INVALIDATE_KEYS,
     onSuccess: () => toast.success(t("plugins.toast.uninstallSuccess")),
+    onError: () => toast.error(t("plugins.toast.uninstallError")),
   });

Note: This requires adding plugins.toast.uninstallError to the i18n locale files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/PluginsView.tsx` around lines 47 - 50, The uninstallMutation
created with useTauriMutation (named uninstallMutation) lacks an onError handler
so failures produce no user feedback; add an onError callback to the mutation
options that calls toast.error(t("plugins.toast.uninstallError")) and optionally
logs the error, keeping existing invalidateKeys/STORE_INVALIDATE_KEYS and
onSuccess intact, and add the plugins.toast.uninstallError key to the i18n
locale files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/views/PluginsView.tsx`:
- Around line 26-28: The isInstalled function in PluginsView duplicates the
logic of isInstalledLike in PluginStoreRow; extract the shared logic into a
single utility function (e.g., export function isInstalledLike(status:
PluginStoreEntry["status"]) { ... }) and replace both the existing isInstalled
in PluginsView and isInstalledLike in PluginStoreRow to import and call that
shared utility; update imports in PluginsView and PluginStoreRow to use the new
exported function and remove the duplicate implementation.
- Around line 47-50: The uninstallMutation created with useTauriMutation (named
uninstallMutation) lacks an onError handler so failures produce no user
feedback; add an onError callback to the mutation options that calls
toast.error(t("plugins.toast.uninstallError")) and optionally logs the error,
keeping existing invalidateKeys/STORE_INVALIDATE_KEYS and onSuccess intact, and
add the plugins.toast.uninstallError key to the i18n locale files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e33f09e7-261d-43a2-a95b-55a91c450848

📥 Commits

Reviewing files that changed from the base of the PR and between 7e46725 and 831ceb6.

📒 Files selected for processing (5)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/views/PluginsView.tsx
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/views/PluginsView/__tests__/PluginStoreRow.test.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/views/PluginsView/tests/PluginStoreRow.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/views/PluginsView/PluginStoreRow.tsx">

<violation number="1" location="src/views/PluginsView/PluginStoreRow.tsx:109">
P1: Installed plugin rows no longer expose a disable action, so users cannot trigger plugin disable from the catalog UI.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/views/PluginsView/PluginStoreRow.tsx Outdated
cubic-dev-ai flagged that removing the disable menu item in 831ceb6
regressed a feature users had before the PR. The original catalog
had a "Désactiver" button that called plugin_disable.

Add it back to the kebab menu, alongside uninstall. The UX caveat
raised earlier (no persistent visual state because the DTO has no
"disabled" field) is documented as a comment next to the mutation
declaration — it's an architectural gap that needs a backend change,
not something to fix by hiding the feature.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 (2)
src/views/PluginsView/__tests__/PluginStoreRow.test.tsx (2)

33-37: Test name and assertions are slightly out of sync.

Line 33 says category is verified, but Lines 35–36 only assert name/description. Either rename the test or add a category assertion.

Proposed tidy-up
-  it("renders the plugin name, description and category", () => {
+  it("renders the plugin name and description", () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/PluginsView/__tests__/PluginStoreRow.test.tsx` around lines 33 -
37, The test titled "renders the plugin name, description and category" is only
asserting the name and description; update the test to either rename it to
accurately reflect what's asserted (e.g., "renders the plugin name and
description") or add an assertion for the category by using the same helpers as
the existing assertions (e.g., call renderRow() and add a screen.getByText(...)
for the category string to match how name/description are checked). Ensure you
update the test title string and/or include the category assertion near the
existing expect calls (renderRow, screen.getByText).

39-42: Make this locale-sensitive assertion deterministic.

Line 41 hardcodes English (Crawlers). If test language is switched to French in shared state, this can become flaky. Pin language to en in this suite (or assert using locale-agnostic strategy).

Example stabilization
-import { describe, it, expect, vi } from "vitest";
+import { describe, it, expect, vi, beforeEach } from "vitest";
@@
 describe("PluginStoreRow", () => {
+  beforeEach(() => {
+    window.localStorage.setItem("i18nextLng", "en");
+  });
+
   it("renders the plugin name, description and category", () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/PluginsView/__tests__/PluginStoreRow.test.tsx` around lines 39 -
42, The test currently asserts a hardcoded English label ("Crawlers") which
makes it flaky when the shared i18n language changes; update the test suite to
pin the locale to English before rendering (e.g., set the i18n language to 'en'
in the beforeEach or the test) or change the assertion to a locale-agnostic
check (e.g., assert on the category key or data attribute instead of the
translated string); locate the assertion in PluginStoreRow.test.tsx that calls
renderRow({ category: "crawler" }) and replace the hardcoded-text expectation
with either a pinned i18n setup or an assertion that does not depend on the
translated label.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/views/PluginsView/__tests__/PluginStoreRow.test.tsx`:
- Around line 95-96: The French matcher in PluginStoreRow.test.tsx uses an ASCII
apostrophe in the regex passed to screen.getByRole to match "plus d'actions" and
similarly for the disable menuitem; update those regexes to accept both ASCII
(') and typographic (’) apostrophes (e.g. use a character class like ['\u2019]
or an alternation) so the queries still match translations using ’; apply the
same fix to the second occurrence around the menuitem disable matcher.

---

Nitpick comments:
In `@src/views/PluginsView/__tests__/PluginStoreRow.test.tsx`:
- Around line 33-37: The test titled "renders the plugin name, description and
category" is only asserting the name and description; update the test to either
rename it to accurately reflect what's asserted (e.g., "renders the plugin name
and description") or add an assertion for the category by using the same helpers
as the existing assertions (e.g., call renderRow() and add a
screen.getByText(...) for the category string to match how name/description are
checked). Ensure you update the test title string and/or include the category
assertion near the existing expect calls (renderRow, screen.getByText).
- Around line 39-42: The test currently asserts a hardcoded English label
("Crawlers") which makes it flaky when the shared i18n language changes; update
the test suite to pin the locale to English before rendering (e.g., set the i18n
language to 'en' in the beforeEach or the test) or change the assertion to a
locale-agnostic check (e.g., assert on the category key or data attribute
instead of the translated string); locate the assertion in
PluginStoreRow.test.tsx that calls renderRow({ category: "crawler" }) and
replace the hardcoded-text expectation with either a pinned i18n setup or an
assertion that does not depend on the translated label.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 986283ae-4269-43fb-a0bc-623bba28ae0c

📥 Commits

Reviewing files that changed from the base of the PR and between 831ceb6 and b25ee26.

📒 Files selected for processing (5)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/views/PluginsView.tsx
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/views/PluginsView/__tests__/PluginStoreRow.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/i18n/locales/en.json
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/i18n/locales/fr.json
  • src/views/PluginsView.tsx

Comment thread src/views/PluginsView/__tests__/PluginStoreRow.test.tsx Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/views/PluginsView.tsx">

<violation number="1" location="src/views/PluginsView.tsx:139">
P2: The new `onDisable` wiring exposes a disable action that cannot be persisted in UI state with the current DTO, so users can trigger disable but still see the plugin as installed after refetch.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/views/PluginsView.tsx
cubic-dev-ai flagged (again) that the Disable action has no UI reflect
because PluginStoreEntryDto lacks a disabled variant. Instead of hiding
the feature, carry the state on the frontend:

- PluginsView keeps a session-local Set<string> of disabled names.
- disableMutation.onSuccess adds the name; enableMutation.onSuccess
  removes it.
- PluginStoreRow accepts isLocallyDisabled + onEnable. When set, the
  row renders dimmed, shows an "inactive" badge, and the kebab swaps
  "Disable" for "Enable".
- enabledCount in the header excludes locally-disabled plugins so the
  counter matches what the user just did.

The state doesn't persist across reloads — that's on purpose, same as
pre-PR behaviour. When the DTO grows a disabled field we can replace
the Set with a derived value.

Also fixes the French matcher regex in the row tests to accept the
typographic apostrophe (coderabbit).
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/views/PluginsView.tsx`:
- Around line 78-81: The uninstallMutation's onSuccess currently only shows a
toast but doesn't clear the in-memory locallyDisabled state, leaving a plugin
marked inactive after reinstall; update the onSuccess handler of
uninstallMutation to also remove/clear the plugin's entry from the
locallyDisabled state (the state variable/collection tracking disabled plugins)
for the uninstalled plugin (use the passed { name } argument) so the UI and
action state are reconciled when uninstall succeeds.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14655a18-e62d-470f-a7cd-171739d04043

📥 Commits

Reviewing files that changed from the base of the PR and between b25ee26 and d510b3c.

📒 Files selected for processing (5)
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/views/PluginsView.tsx
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/views/PluginsView/__tests__/PluginStoreRow.test.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/i18n/locales/fr.json
  • src/views/PluginsView/PluginStoreRow.tsx
  • src/i18n/locales/en.json
  • src/views/PluginsView/tests/PluginStoreRow.test.tsx

Comment thread src/views/PluginsView.tsx
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/views/PluginsView.tsx">

<violation number="1" location="src/views/PluginsView.tsx:40">
P2: `locallyDisabled` state is not cleared when a plugin is uninstalled. If a user disables a plugin and then uninstalls it, the name persists in the `locallyDisabled` set. Reinstalling the same plugin will incorrectly render it as inactive. The `uninstallMutation.onSuccess` handler should remove the plugin name from `locallyDisabled`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/views/PluginsView.tsx
Uninstalling a plugin removed it from the store but kept its name in
the session-local disabled set. Reinstalling then rendered the row as
inactive until the user reloaded the view.
@mpiton mpiton merged commit 57e16ad into main Apr 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant