# Catálogo

> Cosas para instalar, con buscador e Instalar o Actualizar. Un componente nativo de Pyzed para GUIs de escritorio en Python, con vista previa, ejemplos y su API.

Source: https://albakore.github.io/pyzed/es/library/catalog/

:::tip[En pocas palabras]
Una lista con buscador de complementos, como una tienda, cada uno con un botón Instalar.
:::

`ui.Catalog` es un componente para cosas que instalar (agentes de un registro, servidores MCP, skills): un buscador sobre una lista con desplazamiento en la que cada fila muestra el nombre, la versión (`version`) y la descripción (`description`), y a la derecha Instalar, Instalado o Actualizar a una versión nueva. `items` son diccionarios u objetos con `id`, `name` y, opcionales, `version`, `description`, `installed` (un bool) y `update` (la versión nueva). Instalar y Actualizar llegan a `on_install` con el id en `event.value`; mientras `busy` tiene un id, esa fila muestra un spinner y los demás botones de Instalar se desactivan. `height` es la altura de la lista.

`ui.Catalog` · Categoría: [Agentes de IA](https://albakore.github.io/pyzed/es/library/#agentes-de-ia) · [Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#catalog)

**Código:**

```python
pz.container(ui.Catalog(items=[
    {"id": "gemini", "name": "Gemini CLI", "version": "0.64.0", "description": "A coding agent for the terminal.",
     "installed": True, "update": "0.65.0"},
    {"id": "codex", "name": "Codex CLI", "version": "0.40.0", "description": "Writes and runs code for you."},
    {"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent.",
     "installed": True},
    {"id": "opencode", "name": "OpenCode", "version": "0.12.1", "description": "An agent that edits whole projects."},
], busy="codex", height=220, on_install=on_install), width=460)
```

:::note[Nuevo]
Este componente se añadió hace poco. Cuéntanos qué tal te funciona en el [gestor de incidencias](https://github.com/albakore/pyzed/issues).
:::

## Cuándo usarlo

- Para añadir agentes, servidores MCP, skills o plugins desde una lista, dentro de un diálogo o una página de ajustes.

## Uso

```python
import pyzed as pz
from pyzed import ui

class Agents(pz.App):
    def __init__(self):
        self.registry = load_registry()  # [{"id", "name", "version", "description", "installed"}, ...]
        self.installing = ""

    def view(self):
        return ui.Catalog(items=self.registry, busy=self.installing,
                          on_install=lambda e: self.install(e.value))
```

## Variantes

:::note[Sobre estas capturas]
Cada captura la graba pyzed mismo, fuera de pantalla a 2x, en One Dark o One Light según el tema de este sitio, y recortada al componente para que se vea a su tamaño real. El puntero pasa por encima, pulsa o escribe en él; un componente que no reacciona al puntero recorre algunos temas. Con movimiento reducido ves el primer fotograma. Abre **Código** bajo una captura para ver el fragmento que la dibuja; los nombres libres como `on_save` son tus propios callbacks.
:::

### Estado

Mientras se instala uno, los demás botones de Instalar se desactivan.

```python
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose", "version": "1.9.0",
                                "description": "An open source agent."}],
                         height=50), width=380)
```

```python
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose", "version": "1.9.0",
                                "description": "An open source agent.", "installed": True}],
                         height=50), width=380)
```

```python
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose", "version": "1.9.0",
                                "description": "An open source agent.", "installed": True,
                                "update": "2.0.0"}], height=50), width=380)
```

```python
pz.container(ui.Catalog(items=[
    {"id": "goose", "name": "Goose", "version": "1.9.0", "description": "An open source agent."},
    {"id": "codex", "name": "Codex CLI", "version": "0.40.0", "description": "Writes and runs code."},
], busy="goose", height=100), width=380)
```

```python
pz.container(ui.Catalog(items=[{"id": "goose", "name": "Goose"}], height=50), width=380)
```

## Eventos

Catálogo tiene **1 evento propios**, en la tabla de abajo. Como todo widget, también tiene los **18 eventos comunes** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), que se conectan con `.on(...)`: mira [Eventos](https://albakore.github.io/pyzed/es/guides/events/) y [Eventos de cada componente](https://albakore.github.io/pyzed/es/library/events/).

Es un `pz.Component`, así que también lanza `on_mount` y `on_unmount`, y sus handlers reciben un `pz.Event` cuyo `value` trae el dato.

| Evento | El handler recibe | Cuándo |
| --- | --- | --- |
| `on_install` | `(event)` | Se pulsó Instalar o Actualizar (`event.value` es el id). |

**El handler recibe** es lo que Pyzed le pasa a tu función: `()` es nada. Cualquier handler también puede ser un mensaje (un valor que no se puede llamar), que va a `update()`.

### Manejar los eventos

Los mismos eventos escritos de tres formas. **Imperativa**: métodos de la app que cambian `self`. **Declarativa**: los handlers producen mensajes y un único `update()` decide. **Funcional**: una función pura dibuja un estado inmutable y pide cambios con `set_state(...)`. Mira [Imperativo, declarativo y funcional](https://albakore.github.io/pyzed/es/guides/code-styles/).

**Imperativo:**

```python
class Agents(pz.App):
    def __init__(self):
        self.items = [{"id": "goose", "name": "Goose", "version": "1.9.0"},
                      {"id": "codex", "name": "Codex CLI", "version": "0.40.0"}]
        self.installing = ""

    def install(self, ident):            # on_install: event.value is the item's id
        self.installing = ident

    def view(self):
        return ui.Catalog(items=self.items, busy=self.installing, height=200,
                          on_install=lambda e: self.install(e.value))
```

**Declarativo:**

```python
from dataclasses import dataclass

@dataclass
class Install:
    ident: str

class Agents(pz.App):
    items = [{"id": "goose", "name": "Goose", "version": "1.9.0"}]
    installing = ""

    def update(self, message):
        match message:
            case Install(ident):
                self.installing = ident

    def view(self):
        return ui.Catalog(items=self.items, busy=self.installing, height=200,
                          on_install=lambda e: Install(e.value))
```

**Funcional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    installed: frozenset = frozenset()

ITEMS = [{"id": "goose", "name": "Goose", "version": "1.9.0"}]

def view(state, set_state):
    items = [{**i, "installed": i["id"] in state.installed} for i in ITEMS]
    return ui.Catalog(items=items, height=200,
                      on_install=lambda e: set_state(installed=state.installed | {e.value}))
```

## Propiedades y API

### `ui.Catalog`

```python
class Catalog
```

Un componente para cosas que instalar con un buscador (agentes de un registro, servidores, skills…): cada fila muestra Instalar, Instalado o Actualizar a una versión nueva.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `items` | `None` | Dicts u objetos con `id`, `name` y, opcionalmente, `version`, `description`, `installed` y `update`. |
| `busy` | `""` | El id que se está instalando (un indicador de carga). |
| `height` | `320` | Alto de la lista. |
| `theme` | `None` | Tema. |

Eventos: `on_install` ([eventos](#eventos)).

[Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#catalog)

## Relacionados

- [Fila de servicio](https://albakore.github.io/pyzed/es/library/service-row/): Una fila de ajustes para un servidor MCP, un agente o una skill: estado, interruptor y menú.
- [Campo de búsqueda](https://albakore.github.io/pyzed/es/library/search-input/): Un campo con lupa, para filtrar.
- [Fila de lista](https://albakore.github.io/pyzed/es/library/list-item/): Una fila de lista con icono, texto y un hueco al final.
