# Choose an extension type

Start with what the user should see. Grove has one package format, but its presentation determines how people open and use the extension.

| What you are building | Choose | Typical result |
|---|---|---|
| A dashboard, browser, game, or other persistent custom interface beside a conversation | **Visible Sprig** using `channelPane`, optionally `window` | The extension renders its own HTML UI. |
| A bot console or dashboard that owns several joined channels on one network | **Network tab Sprig** using exclusive `tab` + server scope | Mango runs one visible instance for that IRC session. |
| A picker or generator that briefly opens from the message composer | **Composer Sprig** using `composer` | It returns text to the draft with `grove.composer.complete(...)`. |
| Slash commands, native cards, badges, filters, member actions, or an upload provider | **Headless Root** using `headless` | It has no web UI of its own and contributes native behavior to Mango. |

A settings page is optional configuration UI, not a fourth extension type. Any Sprig or Root may declare `settings` when it needs preferences or credentials.

## Choose the surface first

### Visible Sprig

Use a visible Sprig when the extension itself is something the user looks at or interacts with for more than one quick action.

```json
"presentation": {
  "surfaces": ["channelPane", "window"],
  "defaultSurface": "channelPane"
}
```

Starting the Sprig from a conversation binds it to that conversation. `window` changes where the same content appears; it does not widen what IRC data the extension may access.

Good fits include a channel activity dashboard, a poll editor, a shared-game interface, or a tool that needs its own navigation.

### Network tab Sprig

Use a server-scoped tab when one visible runtime coordinates several joined channels on the same IRC connection:

```json
"presentation": {
  "surfaces": ["tab"],
  "defaultSurface": "tab",
  "scope": "server"
}
```

`tab` is exclusive. Mango runs one instance per package and network, and reopening it selects the existing tab. Request `channel.read` to receive live messages plus the joined-channel roster, and `network.send` only when the extension must explicitly reply into those confirmed joined channels.

### Composer Sprig

Use a composer Sprig for a focused choose-or-generate flow that ends by inserting text into Mango's draft.

```json
"presentation": {
  "surfaces": ["composer"],
  "defaultSurface": "composer"
}
```

`composer` must be the only surface. It opens only after an explicit user action, receives no IRC message stream or implicit send target, and cannot autostart. Finish with `grove.composer.complete(text)` so the user can review and send the result.

Good fits include GIF and emoji pickers, canned-response browsers, link builders, and local text transforms.

### Headless Root

Use a Root when Mango should provide the visible UI, or when the extension reacts without presenting a pane.

```json
"presentation": {
  "surfaces": ["headless"]
}
```

A Root can register slash commands and native cards, publish status badges, add member-menu actions, filter messages, or provide an upload destination. Request only the capabilities used by those integrations.

Good fits include weather commands, message formatters, encryption filters, moderation helpers, and upload providers.

## Then choose the scope

Surface and scope are separate decisions. A surface answers **where is it visible?** Scope answers **which Mango or IRC context is it bound to?**

| Required context | Scope | Notes |
|---|---|---|
| One channel or direct message | Omit `presentation.scope` | Mango binds the instance to the conversation from which it starts. |
| One IRC connection | `"scope": "server"` | One instance for that network session. Appropriate for a network tab, connection-wide commands, or filters. |
| The Mango application, without IRC message access | `"scope": "application"` | Shared configuration and passive integrations such as an upload provider available across networks. |

For example, a connection-wide Root declares:

```json
"presentation": {
  "surfaces": ["headless"],
  "scope": "server"
}
```

Application scope is intentionally not ambient access to every network. It has no IRC message, command, filter, presence, or implicit send route. Use it when a host integration should be configured once and does not need to observe IRC traffic.

## Match capabilities to the feature

Capabilities describe the native operations the extension needs; `hostPermissions` separately lists external HTTPS or WSS origins it may contact.

| Feature | Typical capability |
|---|---|
| Read the bound conversation | `channel.read` or `query.read` |
| Read all live joined channels from server scope | `channel.read` |
| Send to the bound conversation | `channel.send` or `query.send` |
| Create and select a new private conversation while sending | `query.send` and `query.open` |
| Send to an explicit confirmed joined channel from server scope | `network.send` |
| Register slash commands and native cards | `commands.register` |
| Open a service profile from a native member action | `ui.member-actions` and `ui.web-pane`, plus matching `hostPermissions` |
| Publish native status badges | `statusbar` |
| Filter displayed or outgoing messages | `messages.filter.inbound` or `messages.filter.outbound` |
| Contribute an upload destination | `uploads.provide` |
| Upload one file produced by a composer utility through Mango | `uploads.use` |
| Open a system OAuth Authorization Code and PKCE flow | `oauth.authorize` |
| Call a non-CORS HTTPS API through bounded native transport | `network.request` plus matching `hostPermissions` |
| Persist ordinary web data | `storage` |
| Store credentials in Keychain | `storage.secure` |

See [Capabilities](capabilities.md) before finalizing the manifest. A capability must be both declared and accepted by the user, and Mango checks the grant again for every bridge call.

## Still unsure?

Choose the smallest shape that completes the user action:

1. If the user needs to browse or manipulate a custom interface, start with a visible Sprig.
2. If the interface exists only to produce draft text, use a composer Sprig.
3. If native cards, commands, filters, badges, or uploads are enough, use a headless Root.
4. Add a settings page only when configuration is necessary.

Continue with the [Starter recipes](starter-recipes.md) to see each shape as a complete two-file package.
