> ## Documentation Index
> Fetch the complete documentation index at: https://docs.marioded.com/llms.txt
> Use this file to discover all available pages before exploring further.

# UI extension concepts

> Choose the right extension point for catalog buttons and in-playback controls.

# UI extension concepts

Reverse has two different UI systems. Before registering an action, choose the one your player will interact with.

## Catalog menus

The catalog and details screens are inventory menus. A configured item invokes a `MenuAction` registered in the runtime's `MenuActionRegistry`.

```java theme={null}
registry.register("example:show_id", (context, parameters) -> {
    if (context.isActive() && context.hasReplay()) {
        context.player().sendMessage(context.requireReplay().getUniqueId().toString());
    }
});
```

Use this for a details button, an information action, or navigation. Obtain the actual registry from the lobby or standalone player, not a new registry instance that the real menu never uses.

## Playback hotbar

The hotbar is the control inventory shown while a viewer watches a replay. A `LayoutItem` defines its slot and rendering; its click bindings reference `LayoutAction` IDs registered through `LayoutService`.

```java theme={null}
layout.registerAction("example:rewind_round", (context, parameters) -> {
    context.playbackService().seekTo(context.session().getSessionId(), 0);
});
```

Use this for seeking, speed selection, or a game-specific replay control. You can bind an action from YAML or install a rendered item programmatically.

## The differences that matter

| Question | Menu | Hotbar |
| - | - | - |
| Main API | `MenuActionRegistry`, `MenuAction.Context` | `LayoutService`, `LayoutItem.Context` |
| Where it appears | Catalog/details inventory | During playback |
| Configuration | Role's `gui.yml` | `playing` in player `layout.yml` |
| Slot numbering | Zero-based inventory slots | Hotbar slots 1–9 |
| Optional selection | A replay may be absent | Context has an active playback session |
| Runtime | Lobby or standalone player | Player |

Both surfaces use namespaced IDs. Registering an existing ID replaces the existing callback. Namespacing avoids accidental collisions; it does not create automatic plugin ownership or cleanup.

## Keep contexts local to the callback

Contexts describe the interaction currently being handled. Do not retain them as long-lived state or blindly use them from asynchronous completions. Schedule Bukkit work to the main thread and recheck active state, connection, request identity, and access where appropriate.

Shared playback state belongs to the session. A per-viewer hotbar value can remember a chosen interval, but seeking still changes the shared cursor.

For complete registration and cleanup code, see the [UI example](https://docs.marioded.com/examples/ui-extensions.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.