Editor/LibraryListProxy.cs
using System;
using System.Threading.Tasks;

class LibraryListProxy : ListView
{
	public Action<Package> OnLibrarySelected { get; set; }

	public bool ShowInstalled { get; set; }
	public bool ShowAvailable { get; set; }

	public string Filter { get; set; } = "";

	public LibraryListProxy( Widget parent ) : base( parent )
	{
		ItemContextMenu = ShowItemContext;
		ItemSelected = OnItemClicked;

		ItemSize = new Vector2( -1, 64 );
	}

	public void OnItemClicked( object value )
	{
		if ( value is LibraryProject lib )
		{
			OnLibrarySelected?.Invoke( lib.Project.Package );
		}

		if ( value is Package p )
		{
			OnLibrarySelected?.Invoke( p );
		}
	}

	private void ShowItemContext( object obj )
	{
		LibraryProject project = obj as LibraryProject;
		Package package = obj as Package ?? project?.Project.Package;

		var m = new Menu();

		if ( package is not null && package.Org.Ident != "local" )
		{
			m.AddOption( "View in Browser", "public", () => EditorUtility.OpenFolder( package.Url ) );
			m.AddSeparator();
		}

		if ( project is not null )
		{
			m.AddOption( "Project Properties", "tune", () => _ = ProjectSettingsWindow.OpenForProject( project.Project ) );
			m.AddSeparator();
			m.AddOption( "Publish Project", "upload_file", () => _ = ProjectSettingsWindow.OpenForProject( project.Project, "upload" ) );
			m.AddSeparator();
			m.AddOption( "Show in Explorer", "folder", () => EditorUtility.OpenFolder( project.Project.RootDirectory.FullName ) );
		}

		m.OpenAt( Editor.Application.CursorPosition );
	}

	protected override void PaintItem( VirtualWidget item )
	{
		if ( item.Object is LibraryProject c )
			PaintItem( item, c.Project.Package );

		if ( item.Object is Package p )
			PaintItem( item, p );
	}

	private void PaintItem( VirtualWidget item, Package c )
	{
		var rect = item.Rect;

		if ( Paint.HasPressed )
			rect = rect.Shrink( 2, 2, 0, 0 );

		var library = LibrarySystem.All.Where( x => x.Project.Package.Ident == c.Ident && x.Project.Package.Org.Ident == c.Org.Ident && x.Project.Package.Ident != "libraryimporter").FirstOrDefault();

		var pen = Color.White.WithAlpha( 0.6f );

		if ( Paint.HasMouseOver || Paint.HasSelected )
		{
			Paint.SetBrush( Theme.Primary.WithAlpha( Paint.HasMouseOver ? 0.8f : 0.5f ) );
			Paint.ClearPen();
			Paint.DrawRect( rect, 4 );

			pen = Color.White.WithAlpha( Paint.HasMouseOver ? 1 : 0.9f );
			Paint.ClearBrush();
		}

		rect = rect.Shrink( 4 );

		// Icon
		{
			var iconRect = rect;
			iconRect.Width = iconRect.Height;
			iconRect = iconRect.Shrink( 8 );

			Paint.SetBrushAndPen( Theme.Grey.WithAlpha( 0.4f ) );

			if ( !string.IsNullOrWhiteSpace( c.Thumb ) && !c.Thumb.StartsWith( "/" ) )
			{
				Paint.Draw( iconRect, c.Thumb );
			}
			else
			{
				Paint.DrawRect( iconRect, 4 );
			}

			rect.Left = iconRect.Right + 8;
		}

		//header
		{
			rect.Top += 8;

			Paint.SetFont( "Poppins", 11, 450 );
			Paint.Pen = pen;
			var r = Paint.DrawText( rect, c.Title, TextFlag.LeftTop );

			r.Left = r.Right + 8;
			r.Right = rect.Right;
			r.Bottom -= 3;

			rect.Top = r.Bottom + 4;

			var installedVersion = library is not null ? $"v{library?.Version}" : "";

			Paint.Pen = pen.WithAlphaMultiplied( 0.6f );
			Paint.SetDefaultFont();
			Paint.DrawText( r, $"{c.Org.Title} {installedVersion}", TextFlag.LeftBottom );
		}

		// body text
		{
			Paint.Pen = pen.WithAlphaMultiplied( 0.6f );
			Paint.SetDefaultFont();
			Paint.DrawText( rect, c.Summary, TextFlag.LeftTop );
		}
	}

	protected override void OnPaint()
	{
		Paint.ClearPen();
		Paint.SetBrush( Theme.ControlBackground );
		Paint.DrawRect( LocalRect );

		Paint.Antialiasing = true;
		Paint.TextAntialiasing = true;

		base.OnPaint();
	}

	[EditorEvent.Frame]
	public void Frame()
	{
		if ( !Visible ) return;

		if ( ShowInstalled )
		{
			if ( SetContentHash( GetContentHash(), 0.5f ) )
			{
				SetItems(LibrarySystem.All.Where(x => x.Project.Package.Ident != "libraryimporter"));
				SelectItem(LibrarySystem.All.FirstOrDefault(x => x.Project.Package.Ident != "libraryimporter") );
			}
		}

		if ( ShowAvailable )
		{
			if ( SetContentHash( GetContentHash(), 0.5f ) )
			{
				_ = UpdateAsync();
			}
		}
	}

	async Task UpdateAsync()
	{
		var search = "";
		if ( !string.IsNullOrEmpty( Filter ) ) search = Filter + " ";
		var found = await Package.FindAsync( $"{search}type:library sort:updated", 200, 0 );
		SetItems( found.Packages );

		SelectItem( found.Packages.FirstOrDefault() );
	}

	int GetContentHash()
	{
		if ( ShowInstalled )
			return LibrarySystem.All.GetHashCode();

		return HashCode.Combine( Filter );
	}

}