<!-- Source: https://docs.biosimulant.com/references/desktop-plugins/ui-contributions -->

# UI Contributions

Plugins can declare UI contributions in their manifest to appear in different parts of the desktop app. The desktop app renders generic views based on the plugin's `kind` and never imports plugin-specific React components.

## How It Works

1. Your `plugin.json` declares `ui_contributions` with slot names, icons, and labels
2. The desktop app reads these contributions from installed plugins at runtime
3. Based on your plugin's `kind` (e.g., `agent_provider`), the app selects a generic renderer
4. The renderer provides the UI framework; your plugin provides the data via JSON-RPC

**Info:**

  The desktop app has **no compile-time knowledge** of your plugin. All UI is rendered dynamically based on the plugin kind and contributions declared in the manifest.

## Available Slots

### `sidebar.item`

Adds an icon to the main app sidebar, between the core navigation items and the bottom controls. Clicking the icon navigates to your plugin's full-page view.

```json
{
  "slot": "sidebar.item",
  "icon": "wand-sparkles",
  "label": "My Plugin"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `icon` | `string` | No | Lucide icon name. Defaults to `"puzzle"`. |
| `label` | `string` | No | Display label. Defaults to the plugin `name`. |
| `order` | `number` | No | Sort order among plugin sidebar items. Lower values appear first. |

**Supported icon names:** `wand-sparkles`, `bot`, `wrench`, `puzzle`

### `page.main`

Declares that your plugin provides a full-page view accessible at `/plugins/:pluginId`. Requires a matching `sidebar.item` contribution for users to navigate to it.

```json
{
  "slot": "page.main",
  "component": "my-page"
}
```

The `component` value is a label for your own reference. The desktop app selects the renderer based on your plugin's `kind`, not this field.

**Renderers by kind:**

| Kind | Renderer | Description |
|------|----------|-------------|
| `agent_provider` | Agent Plugin Page | Full-page agent view with lab selector and chat interface |
| `tool_provider` | *(coming soon)* | Generic tool page |

### `lab.sidepanel`

Adds a tab to the lab detail right sidebar. Users can switch between Run, Agent, and your plugin's tab while working in a lab.

```json
{
  "slot": "lab.sidepanel",
  "component": "my-panel",
  "icon": "wrench",
  "label": "My Tool"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `component` | `string` | No | Label for your reference. |
| `icon` | `string` | No | Lucide icon name for the tab button. |
| `label` | `string` | No | Tab button label. Defaults to plugin `name`. |

**Renderers by kind:**

| Kind | Renderer | Description |
|------|----------|-------------|
| `agent_provider` | Agent Plugin Panel | Compact agent chat scoped to the current lab |
| `tool_provider` | *(coming soon)* | Generic tool panel |

### `settings.section`

Declares that your plugin contributes a section to the Settings page.

```json
{
  "slot": "settings.section",
  "component": "my-settings"
}
```

**Info:**

  Settings section rendering is planned for a future release. Currently, plugin settings are managed through the Plugins section in Settings (enable/disable, permissions, updates).

## Example: Agent Provider

An `agent_provider` plugin that appears in the sidebar, has a full-page view, and contributes a lab sidepanel tab:

```json
{
  "id": "openai.codex",
  "name": "Codex Workspace Agent",
  "kind": "agent_provider",
  "ui_contributions": [
    { "slot": "sidebar.item", "icon": "wand-sparkles", "label": "Codex" },
    { "slot": "page.main", "component": "codex-page" },
    { "slot": "lab.sidepanel", "component": "codex-panel", "icon": "wand-sparkles", "label": "Codex" }
  ]
}
```

This results in:

```
┌──────────────────────────────────────────────────┐
│ Sidebar (72px)  │  Main Content                  │
│                 │                                │
│  Dashboard      │  (route-dependent)             │
│  Labs           │                                │
│  Models         │                                │
│  Runs           │                                │
│  Hub            │                                │
│  ─────────      │                                │
│  ✨ Codex  ←plugin                               │
│  ─────────      │                                │
│  🧩 Plugins     │                                │
│  Help           │                                │
│  Settings       │                                │
└──────────────────────────────────────────────────┘
```

## Example: Tool Provider

A `tool_provider` plugin with no sidebar icon, only accessible from the Plugins page:

```json
{
  "id": "acme.data-exporter",
  "name": "Data Exporter",
  "kind": "tool_provider",
  "ui_contributions": []
}
```

This plugin is visible in **Settings > Plugins** and **Plugins marketplace** but does not add a sidebar icon or lab panel tab.

## Not Installed Behavior

When a user visits the Plugins marketplace and your plugin is available but not installed, the desktop app shows a generic install card with your plugin's name, description, version, and an **Install** button. No custom UI is needed for this. The marketplace card is generated from your manifest metadata.
