# Edits bar

> The files an agent changed, with Keep all and Reject all. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/edits-bar/

:::tip[In plain words]
A bar that lists the files an AI assistant changed, so you can keep or undo the changes.
:::

`ui.EditsBar` is a component that lists the files an agent changed: `3 files changed` with the lines added and removed, Reject all and Keep all. Clicking the title unfolds one row per file with its diff stat and buttons to reject or keep that file alone; clicking a file's name reports `on_open`. `files` are dicts or objects with `path` and optional `added` and `removed` line counts. The file events (`on_keep_file`, `on_reject_file`, `on_open`) carry the path in `event.value`.

`ui.EditsBar` · Category: [AI agents](https://albakore.github.io/pyzed/library/#ai-agents) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#editsbar)

**Code:**

```python
pz.container(ui.EditsBar(files=[
    {"path": "app/storage.py", "added": 12, "removed": 3},
    {"path": "app/forms/login.py", "added": 40, "removed": 0},
    {"path": "tests/test_login.py", "added": 18, "removed": 2},
], on_keep=on_keep, on_reject=on_reject), width=520)
```

:::note[New]
This component was added recently. Tell us how it works for you in the [issue tracker](https://github.com/albakore/pyzed/issues).
:::

## When to use

- Under an agent's chat, once it has edited files, so the user reviews them before keeping them.

## Usage

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

class Review(pz.App):
    def __init__(self):
        self.files = [{"path": "app/storage.py", "added": 12, "removed": 3}]

    def keep(self, path=None):  # None: every file
        self.files = [f for f in self.files if path is not None and f["path"] != path]

    def view(self):
        return ui.EditsBar(files=self.files, on_keep=lambda e: self.keep(),
                           on_keep_file=lambda e: self.keep(e.value), on_open=lambda e: self.open(e.value))
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### State

Unfolded ones are drawn as they look after a click on the header.

```python
pz.container(ui.EditsBar(files=[{"path": "app.py", "added": 12, "removed": 3},
                             {"path": "ui/form.py", "added": 40, "removed": 0}]),
             width=440)
```

```python
pz.container(ui.EditsBar(files=[{"path": "app.py", "added": 12, "removed": 3},
                             {"path": "ui/form.py", "added": 40, "removed": 0}]),
             width=440)
```

```python
pz.container(ui.EditsBar(files=[{"path": "app.py", "added": 2, "removed": 2}]), width=440)
```

```python
pz.container(ui.EditsBar(files=[{"path": "app.py"}, {"path": "README.md"}]), width=440)
```

## Events

Edits bar has **5 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

It is a `pz.Component`, so it also raises `on_mount` and `on_unmount`, and its handlers get a `pz.Event` whose `value` holds the data.

| Event | Handler gets | When |
| --- | --- | --- |
| `on_keep` | `(event)` | Keep all. |
| `on_reject` | `(event)` | Reject all. |
| `on_keep_file` | `(event)` | Keep on a file (`event.value` is its path). |
| `on_reject_file` | `(event)` | Reject on a file. |
| `on_open` | `(event)` | A file was clicked. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Review(pz.App):
    def __init__(self):
        self.files = [{"path": "app/storage.py", "added": 12, "removed": 3},
                      {"path": "tests/test_storage.py", "added": 8, "removed": 0}]

    def drop(self, path=None):           # None: every file
        self.files = [f for f in self.files if path is not None and f["path"] != path]

    def view(self):                      # event.value is the file's path
        return ui.EditsBar(files=self.files,
                           on_keep=lambda e: self.drop(), on_reject=lambda e: self.drop(),
                           on_keep_file=lambda e: self.drop(e.value),
                           on_reject_file=lambda e: self.drop(e.value),
                           on_open=lambda e: print("open", e.value))
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class Done:
    path: str = ""

class Review(pz.App):
    files = [{"path": "app/storage.py", "added": 12, "removed": 3}]

    def update(self, message):
        match message:
            case Done(""):
                self.files = []
            case Done(path):
                self.files = [f for f in self.files if f["path"] != path]

    def view(self):
        return ui.EditsBar(files=self.files, on_keep=Done(), on_keep_file=lambda e: Done(e.value))
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    files: tuple = ({"path": "app/storage.py", "added": 12, "removed": 3},)

def view(state, set_state):
    without = lambda path: tuple(f for f in state.files if f["path"] != path)
    return ui.EditsBar(files=list(state.files), on_keep=lambda e: set_state(files=()),
                       on_keep_file=lambda e: set_state(files=without(e.value)))
```

## Properties and API

### `ui.EditsBar`

```python
class EditsBar
```

A component listing the files an agent changed, like Zed's edits bar: `3 files changed` with Reject all and Keep all, unfolding to one row per file with its diff stat, Reject and Keep.

| Property | Default | Description |
| --- | --- | --- |
| `files` | `None` | Dicts or objects with `path` and optional `added` and `removed` line counts. |
| `theme` | `None` | Theme. |

Events: `on_keep`, `on_reject`, `on_keep_file`, `on_reject_file`, `on_open` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#editsbar)

## Related

- [Diff view](https://albakore.github.io/pyzed/library/diff-view/): An edit as colored lines: added in green, removed in red.
- [Diff stat](https://albakore.github.io/pyzed/library/diff-stat/): `+12 −3` with GitHub-style blocks.
- [Tool call](https://albakore.github.io/pyzed/library/tool-call/): One step of an AI agent: what it did, its result, and Allow or Reject.
