# Historial de hilos

> Conversaciones pasadas, con búsqueda y agrupadas por día. 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/thread-history/

:::tip[En pocas palabras]
La lista de tus chats anteriores, ordenados por día, con una caja para buscarlos.
:::

`ui.ThreadHistory` es un componente para conversaciones pasadas: un buscador y los hilos agrupados según cuándo se actualizaron (Hoy, Ayer, Esta semana, Este mes, Antes). Cada fila muestra el título, un `detail` opcional (el modelo o el agente) y un botón para borrarlo. `threads` son diccionarios u objetos con `id`, `title`, `updated` (una marca de `time.time()`) y `detail`; `selected` marca el que está abierto. Elegir un hilo llega a `on_open` y el botón de la papelera a `on_delete`, los dos con el id del hilo en `event.value`. El componente guarda el texto de búsqueda por su cuenta.

`ui.ThreadHistory` · 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/#threadhistory)

**Código:**

```python
import time

now = time.time()
pz.container(ui.ThreadHistory(threads=[
    {"id": "1", "title": "Add a login form", "updated": now - 60, "detail": "Claude"},
    {"id": "2", "title": "Fix the failing tests", "updated": now - 900, "detail": "Claude"},
    {"id": "3", "title": "Dark theme for the settings", "updated": now - 86400, "detail": "Gemini CLI"},
    {"id": "4", "title": "Export the table to CSV", "updated": now - 4 * 86400},
    {"id": "5", "title": "First steps", "updated": now - 60 * 86400},
], selected="1", on_open=on_open, on_delete=on_delete), width=380, height=320)
```

:::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

- En un panel lateral o un popover de una app de chat, para volver a abrir una conversación anterior.

## Uso

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

class Chats(pz.App):
    def __init__(self):
        self.threads = load_threads()  # [{"id", "title", "updated", "detail"}, ...]
        self.open_id = ""

    def delete(self, ident):
        self.threads = [t for t in self.threads if t["id"] != ident]

    def view(self):
        return ui.ThreadHistory(threads=self.threads, selected=self.open_id,
                                on_open=lambda e: setattr(self, "open_id", e.value),
                                on_delete=lambda e: self.delete(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

Las de búsqueda se dibujan con texto escrito en la caja (el componente lo guarda).

```python
import time

now = time.time()
threads = [
    {"id": "1", "title": "Add a login form", "updated": now - 60, "detail": "Claude"},
    {"id": "2", "title": "Fix the failing tests", "updated": now - 86400},
    {"id": "3", "title": "Export the table to CSV", "updated": now - 4 * 86400},
]
pz.container(ui.ThreadHistory(threads=threads), width=320, height=260)
```

```python
import time

now = time.time()
threads = [
    {"id": "1", "title": "Add a login form", "updated": now - 60, "detail": "Claude"},
    {"id": "2", "title": "Fix the failing tests", "updated": now - 86400},
    {"id": "3", "title": "Export the table to CSV", "updated": now - 4 * 86400},
]
pz.container(ui.ThreadHistory(threads=threads, selected="2"), width=320, height=260)
```

```python
pz.container(ui.ThreadHistory(threads=[]), width=320, height=110)
```

```python
import time

now = time.time()
threads = [
    {"id": "1", "title": "Add a login form", "updated": now - 60, "detail": "Claude"},
    {"id": "2", "title": "Fix the failing tests", "updated": now - 86400},
    {"id": "3", "title": "Export the table to CSV", "updated": now - 4 * 86400},
]
pz.container(ui.ThreadHistory(threads=threads), width=320, height=160)
```

```python
import time

now = time.time()
threads = [
    {"id": "1", "title": "Add a login form", "updated": now - 60, "detail": "Claude"},
    {"id": "2", "title": "Fix the failing tests", "updated": now - 86400},
    {"id": "3", "title": "Export the table to CSV", "updated": now - 4 * 86400},
]
pz.container(ui.ThreadHistory(threads=threads), width=320, height=110)
```

## Eventos

Historial de hilos tiene **2 eventos 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_open` | `(event)` | Se eligió un hilo (`event.value` es su id). |
| `on_delete` | `(event)` | Se pulsó eliminar en un hilo. |

**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
import time

class Chats(pz.App):
    def __init__(self):
        now = time.time()
        self.threads = [{"id": "1", "title": "Add a login form", "updated": now - 60},
                        {"id": "2", "title": "Fix the tests", "updated": now - 900}]
        self.open_id = "1"

    def view(self):                      # event.value is the thread's id
        return ui.ThreadHistory(threads=self.threads, selected=self.open_id,
                                on_open=lambda e: setattr(self, "open_id", e.value),
                                on_delete=lambda e: self.delete(e.value))

    def delete(self, ident):
        self.threads = [t for t in self.threads if t["id"] != ident]
```

**Declarativo:**

```python
from dataclasses import dataclass

@dataclass
class OpenThread:
    ident: str

@dataclass
class DeleteThread:
    ident: str

class Chats(pz.App):
    threads = [{"id": "1", "title": "Add a login form", "updated": 0}]
    open_id = ""

    def update(self, message):
        match message:
            case OpenThread(ident):
                self.open_id = ident
            case DeleteThread(ident):
                self.threads = [t for t in self.threads if t["id"] != ident]

    def view(self):
        return ui.ThreadHistory(threads=self.threads, selected=self.open_id,
                                on_open=lambda e: OpenThread(e.value), on_delete=lambda e: DeleteThread(e.value))
```

**Funcional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    threads: tuple = ({"id": "1", "title": "Add a login form", "updated": 0},)
    open_id: str = ""

def view(state, set_state):
    return ui.ThreadHistory(
        threads=list(state.threads), selected=state.open_id,
        on_open=lambda e: set_state(open_id=e.value),
        on_delete=lambda e: set_state(threads=tuple(t for t in state.threads if t["id"] != e.value)))
```

## Propiedades y API

### `ui.ThreadHistory`

```python
class ThreadHistory
```

Un componente para conversaciones anteriores: un buscador y los hilos agrupados por día (Hoy, Ayer, Esta semana, Este mes, Antes), cada uno con un botón para eliminarlo.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `threads` | `None` | Dicts u objetos con `id`, `title`, `updated` (una marca de tiempo) y un `detail` opcional. |
| `selected` | `""` | El id del hilo abierto. |
| `theme` | `None` | Tema. |

Eventos: `on_open`, `on_delete` ([eventos](#eventos)).

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

## Relacionados

- [Fila de lista](https://albakore.github.io/pyzed/es/library/list-item/): Una fila de lista con icono, texto y un hueco al final.
- [Campo de búsqueda](https://albakore.github.io/pyzed/es/library/search-input/): Un campo con lupa, para filtrar.
- [Lista virtual](https://albakore.github.io/pyzed/es/library/virtual-list/): Una lista que solo construye las filas visibles, para millones de elementos.
