TUI Plugin Sharing 5 - polish remote plugin catalog rows (#26705)

This is the final plugin sharing PR in the 5-PR stack. It applies the
remaining TUI polish for remote plugin catalog rows and tabs:
admin-disabled plugins now read as blocked/view-only instead of looking
toggleable, admin-installed/default-installed plugins count and sort
like installed plugins, plugin search matches richer metadata, and an
empty successful `Shared with me` section stays hidden.

- Admin-disabled rows use a blocked marker, show `Disabled`, and keep
Enter-only detail behavior without a toggle hint.
- Admin-installed/default-installed plugins show as installed in counts,
ordering, tabs, and detail copy.
- Plugin search now matches descriptions and keywords in addition to
existing row metadata.
- Successful-empty `Shared with me` tabs are hidden, while loading,
error, workspace-empty, and real shared-plugin states remain visible.
- Updates coverage in
`plugins_popup_snapshot_shows_all_marketplaces_and_sorts_installed_then_name`,
`plugins_popup_admin_disabled_installed_plugin_has_no_toggle_hint`,
`plugins_popup_search_matches_plugin_descriptions`, and
`plugins_popup_remote_section_fallback_states_snapshot`.
- Updates snapshots `plugins_popup_curated_marketplace` and
`plugins_popup_empty_shared_section_hidden`.


<img width="2034" height="106" alt="image"
src="https://github.com/user-attachments/assets/3f9a57e1-edd8-4e6c-b0b0-9f632a3c9529"
/>
<img width="2038" height="380" alt="image"
src="https://github.com/user-attachments/assets/45a47491-3381-4846-a13d-496bc0051d42"
/>
This commit is contained in:
canvrno-oai authored and GitHub committed 2026-06-24 18:48:11 -07:00
1 parent f15df624a6
commit a0d5fd772e
12 files changed
+362 -112

No files matched your search

@@ -281,6 +281,63 @@ pub(crate) struct ListSelectionView {
keymap: ListKeymap,
}
const SELECTION_TOGGLE_ON_PREFIX: &str = "[*] ";
const SELECTION_TOGGLE_OFF_PREFIX: &str = "[ ] ";
pub(crate) const SELECTION_TOGGLE_UNAVAILABLE_PREFIX: &str = "[-] ";
pub(crate) const SELECTION_TOGGLE_BLOCKED_PREFIX: &str = "[!] ";
fn selection_toggle_prefix(toggle: &SelectionToggle) -> &'static str {
if toggle.is_on {
SELECTION_TOGGLE_ON_PREFIX
} else {
SELECTION_TOGGLE_OFF_PREFIX
}
}
fn selection_item_toggle_prefix(item: &SelectionItem) -> Option<&'static str> {
item.toggle
.as_ref()
.map(selection_toggle_prefix)
.or(item.toggle_placeholder)
}
impl ListSelectionView {
fn selected_item_has_toggle(&self) -> bool {
self.selected_actual_idx()
.and_then(|actual_idx| self.active_items().get(actual_idx))
.is_some_and(|item| item.toggle.is_some() && Self::item_is_enabled(item))
}
fn selected_item_has_toggle_placeholder(&self) -> bool {
self.selected_actual_idx()
.and_then(|actual_idx| self.active_items().get(actual_idx))
.is_some_and(|item| {
item.toggle.is_none()
&& item.toggle_placeholder.is_some()
&& Self::item_is_enabled(item)
})
}
fn toggle_selected(&mut self) {
let Some(actual_idx) = self.selected_actual_idx() else {
return;
};
let app_event_tx = self.app_event_tx.clone();
let Some(item) = self.active_items_mut().get_mut(actual_idx) else {
return;
};
if !Self::item_is_enabled(item) {
return;
}
let Some(toggle) = item.toggle.as_mut() else {
return;
};
toggle.is_on = !toggle.is_on;
(toggle.action)(toggle.is_on, &app_event_tx);
}
}
impl ListSelectionView {
/// Create a selection popup view with filtering, scrolling, and callbacks wired.
///
@@ -533,10 +590,8 @@ impl ListSelectionView {
let wrap_prefix_width = UnicodeWidthStr::width(wrap_prefix.as_str());
let mut name_prefix_spans = Vec::new();
name_prefix_spans.push(wrap_prefix.into());
if let Some(toggle) = &item.toggle {
name_prefix_spans.push(if toggle.is_on { "[*] " } else { "[ ] " }.into());
} else if let Some(placeholder) = item.toggle_placeholder {
name_prefix_spans.push(placeholder.into());
if let Some(toggle_prefix) = selection_item_toggle_prefix(item) {
name_prefix_spans.push(toggle_prefix.into());
}
name_prefix_spans.extend(item.name_prefix_spans.clone());
let description = is_selected
@@ -611,22 +666,6 @@ impl ListSelectionView {
item.disabled_reason.is_none() && !item.is_disabled
}
fn selected_item_has_toggle(&self) -> bool {
self.selected_actual_idx()
.and_then(|actual_idx| self.active_items().get(actual_idx))
.is_some_and(|item| item.toggle.is_some() && Self::item_is_enabled(item))
}
fn selected_item_has_toggle_placeholder(&self) -> bool {
self.selected_actual_idx()
.and_then(|actual_idx| self.active_items().get(actual_idx))
.is_some_and(|item| {
item.toggle.is_none()
&& item.toggle_placeholder.is_some()
&& Self::item_is_enabled(item)
})
}
fn actual_idx_for_enabled_number(&self, number: usize) -> Option<usize> {
if number == 0 {
return None;
@@ -640,25 +679,6 @@ impl ListSelectionView {
.map(|(idx, _)| idx)
}
fn toggle_selected(&mut self) {
let Some(actual_idx) = self.selected_actual_idx() else {
return;
};
let app_event_tx = self.app_event_tx.clone();
let Some(item) = self.active_items_mut().get_mut(actual_idx) else {
return;
};
if !Self::item_is_enabled(item) {
return;
}
let Some(toggle) = item.toggle.as_mut() else {
return;
};
toggle.is_on = !toggle.is_on;
(toggle.action)(toggle.is_on, &app_event_tx);
}
fn move_up(&mut self) {
let before = self.selected_actual_idx();
let len = self.visible_len();
+2
View File
@@ -190,6 +190,8 @@ use crate::status_indicator_widget::StatusDetailsCapitalization;
use crate::status_indicator_widget::StatusIndicatorWidget;
pub(crate) use experimental_features_view::ExperimentalFeatureItem;
pub(crate) use experimental_features_view::ExperimentalFeaturesView;
pub(crate) use list_selection_view::SELECTION_TOGGLE_BLOCKED_PREFIX;
pub(crate) use list_selection_view::SELECTION_TOGGLE_UNAVAILABLE_PREFIX;
pub(crate) use list_selection_view::SelectionAction;
pub(crate) use list_selection_view::SelectionItem;