3939from data_designer .config .utils .constants import NordColor
4040
4141NARROW_CATALOG_LAYOUT_WIDTH = 100
42+ CATALOG_TABLE_ROW_LEADING = 1
43+ CHECKMARK = "✓"
44+ X_MARK = "x"
4245
4346
4447class PluginCatalogController :
@@ -66,14 +69,15 @@ def run_list(
6669 entries = self ._list_entries_or_exit (catalog .alias , refresh = refresh , include_incompatible = include_incompatible )
6770
6871 print_header ("Data Designer Plugin Packages" )
69- print_info (f"Catalog: { catalog .alias } ({ catalog .url } )" )
7072 console .print ()
7173
7274 if not entries :
7375 self ._display_empty_list_state (catalog .alias , include_incompatible = include_incompatible )
76+ _print_catalog_reference (catalog )
7477 return
7578
7679 self ._display_catalog_entries (entries )
80+ _print_catalog_reference (catalog )
7781
7882 def run_search (
7983 self ,
@@ -93,7 +97,6 @@ def run_search(
9397 )
9498
9599 print_header ("Data Designer Plugin Package Search" )
96- print_info (f"Catalog: { catalog .alias } ({ catalog .url } )" )
97100 print_info (f"Query: { query } " )
98101 console .print ()
99102
@@ -103,9 +106,11 @@ def run_search(
103106 catalog .alias ,
104107 include_incompatible = include_incompatible ,
105108 )
109+ _print_catalog_reference (catalog )
106110 return
107111
108112 self ._display_catalog_entries (entries )
113+ _print_catalog_reference (catalog )
109114
110115 def run_info (
111116 self ,
@@ -530,15 +535,21 @@ def _suggest_entries(
530535 return []
531536
532537 def _display_catalog_entries (self , entries : list [PluginCatalogEntry ]) -> None :
538+ installed_plugins = self .catalog_service .list_installed_plugins ()
533539 if _console_width () < NARROW_CATALOG_LAYOUT_WIDTH :
534- self ._display_catalog_entries_vertical (entries )
540+ self ._display_catalog_entries_vertical (entries , installed_plugins )
535541 return
536542
537- table = Table (title = "Catalog Plugin Packages" , border_style = NordColor .NORD8 .value )
543+ table = Table (
544+ title = "Catalog Plugin Packages" ,
545+ border_style = NordColor .NORD8 .value ,
546+ leading = CATALOG_TABLE_ROW_LEADING ,
547+ )
538548 table .add_column ("Package" , style = NordColor .NORD14 .value , no_wrap = True )
539549 table .add_column ("Description" , style = NordColor .NORD4 .value )
540550 table .add_column ("Runtime Plugins" , style = NordColor .NORD9 .value )
541- table .add_column ("Compatible" , style = NordColor .NORD13 .value , no_wrap = True )
551+ table .add_column ("Compatible" , style = NordColor .NORD13 .value , justify = "center" , no_wrap = True )
552+ table .add_column ("Installed" , style = NordColor .NORD14 .value , justify = "center" , no_wrap = True )
542553 table .add_column ("Docs" , style = NordColor .NORD7 .value )
543554
544555 for package_entries in self .catalog_service .group_entries_by_package (entries ).values ():
@@ -549,12 +560,17 @@ def _display_catalog_entries(self, entries: list[PluginCatalogEntry]) -> None:
549560 entry .package .name ,
550561 entry .description ,
551562 _format_runtime_plugins (package_entries ),
552- "yes" if compatibility .is_compatible else "no" ,
563+ _format_compatibility_marker (compatibility ),
564+ _format_installed_marker (package_entries , installed_plugins ),
553565 _format_docs_link (docs_url ),
554566 )
555567 console .print (table )
556568
557- def _display_catalog_entries_vertical (self , entries : list [PluginCatalogEntry ]) -> None :
569+ def _display_catalog_entries_vertical (
570+ self ,
571+ entries : list [PluginCatalogEntry ],
572+ installed_plugins : list [InstalledPluginInfo ],
573+ ) -> None :
558574 for index , package_entries in enumerate (self .catalog_service .group_entries_by_package (entries ).values ()):
559575 entry = package_entries [0 ]
560576 compatibility = self .catalog_service .evaluate_compatibility (entry )
@@ -564,7 +580,8 @@ def _display_catalog_entries_vertical(self, entries: list[PluginCatalogEntry]) -
564580 console .print (Text (entry .package .name , style = f"bold { NordColor .NORD14 .value } " ))
565581 console .print (f" Description: { entry .description } " )
566582 console .print (f" Runtime plugins: { _format_runtime_plugins (package_entries )} " )
567- console .print (f" Compatible: { 'yes' if compatibility .is_compatible else 'no' } " )
583+ console .print (f" Compatible: { _format_compatibility_marker (compatibility )} " )
584+ console .print (f" Installed: { _format_installed_marker (package_entries , installed_plugins )} " )
568585 if docs_url :
569586 console .print (f" Docs: { docs_url } " )
570587
@@ -606,6 +623,16 @@ def _display_commands(commands: list[list[str]]) -> None:
606623 console .print (f" [bold]{ shlex .join (command )} [/bold]" )
607624
608625
626+ def _print_catalog_reference (catalog : PluginCatalogConfig ) -> None :
627+ console .print ()
628+ catalog_link = Text .assemble (
629+ " 🗂️ Catalog: " ,
630+ (catalog .alias , Style (color = NordColor .NORD14 .value , bold = True , link = catalog .url )),
631+ )
632+ console .print (catalog_link )
633+ console .print ()
634+
635+
609636def _target_description (mode : str , project_root : str | None ) -> str :
610637 if mode == "uv-project" and project_root is not None :
611638 return f"current uv project ({ project_root } )"
@@ -616,6 +643,31 @@ def _format_runtime_plugins(entries: list[PluginCatalogEntry]) -> str:
616643 return ", " .join (f"{ entry .name } ({ entry .plugin_type .value } )" for entry in entries )
617644
618645
646+ def _format_checkmark (value : bool ) -> str :
647+ return CHECKMARK if value else ""
648+
649+
650+ def _format_compatibility_marker (compatibility : CompatibilityResult ) -> str :
651+ return CHECKMARK if compatibility .is_compatible else X_MARK
652+
653+
654+ def _format_installed_marker (
655+ package_entries : list [PluginCatalogEntry ],
656+ installed_plugins : list [InstalledPluginInfo ],
657+ ) -> str :
658+ return _format_checkmark (_package_entries_are_installed (package_entries , installed_plugins ))
659+
660+
661+ def _package_entries_are_installed (
662+ package_entries : list [PluginCatalogEntry ],
663+ installed_plugins : list [InstalledPluginInfo ],
664+ ) -> bool :
665+ installed_entry_points = {(plugin .name , plugin .entry_point_value ) for plugin in installed_plugins }
666+ return bool (package_entries ) and all (
667+ (entry .entry_point .name , entry .entry_point .value ) in installed_entry_points for entry in package_entries
668+ )
669+
670+
619671def _package_alias (package_name : str ) -> str | None :
620672 canonical_package_name = canonicalize_name (package_name )
621673 if not canonical_package_name .startswith (DATA_DESIGNER_PLUGIN_PACKAGE_PREFIX ):
0 commit comments