# Addon API

Source: https://docs.gryt.chat/docs/client/addon-api

Everything on the gryt object a client plugin runs against, generated from the source

{/* Generated by .github/scripts/generate-api-reference.mjs in the monorepo.
    Do not edit this file. Change the source it reads, then run the script. */}

This is the reference. [Addons](https://docs.gryt.chat/docs/client/addons) is the page that teaches
the shape, and [plugin pairs](https://docs.gryt.chat/docs/guide/plugin-pairs) covers the server half.

A plugin runs in a worker of its own, and `gryt` is a global in it. There is no
`window.gryt` and no addon id on any call — Gryt already knows which plugin is
asking.

## What is not there

A worker has no page, and the rest is taken away before your module is imported:

```js
window        // a worker has none
document
localStorage
indexedDB     // deleted off the prototype chain, not just off globalThis
caches
Worker        // no nesting out of it
```

Your network is not. Whatever you are granted, you can send anywhere, and that
is the part worth telling people who install your plugin.

## Capabilities

Declared in the manifest and agreed to per addon. Both, or the call rejects and
says which is missing.

| Capability | What the person reads |
| --- | --- |
| `status` | Set what you are doing, on every server you are on |
| `messaging` | Exchange its own messages with the servers you are on |
| `display` | Show its own panel beside the member list |
| `processes` | See which of your listed programs are running |

## The manifest

`manifest.json`, in your addon's folder.

| Field | Type | What it is |
| --- | --- | --- |
| `id` | `string` |  |
| `name` | `string` |  |
| `version` | `string` |  |
| `type` | `"plugin" \| "theme"` |  |
| `description?` | `string` |  |
| `author?` | `string` |  |
| `banner?` | `string` |  |
| `styles?` | `string[]` | Theme-only: CSS files to inject |
| `main?` | `string` | Plugin-only: JS entry point |
| `requiresReloadOnDisable?` | `boolean` | Plugin-only: if true, disabling the addon reloads the client |
| `capabilities?` | `string[]` | Plugin-only: what it says it needs to do (GRYT-928). Declared here and agreed to per addon before `window.gryt` will answer. **Not a sandbox** — see `capabilities.ts` for what this does and does not buy. A plugin runs in the app's own page and could go around it. Unknown names are dropped rather than refused, so a manifest written against a newer Gryt still loads on an older one. |
| `repository?` | `string` | Where the addon is published, as `owner/repo` on GitHub. Optional. An addon without it never reports an update, which is the right default for one somebody wrote for themselves and dropped in the folder. Not free-form: this string decides what gets fetched, so a manifest whose repository is anything other than two plain path segments is rejected when it is read rather than when it is used. |

## `gryt`

### `version`

```ts
version: string
```

### `theme`

```ts
theme: ThemeInfo
```

### `on()`

```ts
on(event: "themeChange" | "cleanup", handler: (payload: ThemeInfo | undefined) => void): () => void
```

Hear about something.

`themeChange` when the app's appearance or accent changes, and `cleanup`
when this plugin is being turned off — which is the only chance it gets to
clear a status or stop a timer. Whatever it does not finish quickly enough
happens anyway: the worker is terminated shortly after, from outside.

### `setActivity()`

```ts
setActivity(activity: string): Promise<unknown>
```

Needs `status`.

Say what the person running this is doing.

An empty string clears it. Rejects if the capability was not granted,
rather than resolving quietly — a status that never appears is harder to
work out than an error.

### `gryt.messaging`

#### `send()`

```ts
send(topic: string, data: unknown, host?: string): Promise<unknown>
```

Needs `messaging`.

Send to the copy of this plugin on the server.

#### `on()`

```ts
on(topic: string, handler: (message: unknown) => void): () => void
```

Hear from it. Needs `messaging`.

#### `servers()`

```ts
servers(): Promise<string[]>
```

Needs `messaging`.

Which servers run the other half. A round trip, so a promise.

### `gryt.processes`

#### `running()`

```ts
running(): Promise<string[]>
```

Needs `processes`.

Which of the programs the person listed are running.

Their list, not their machine. Somebody who has written down two games
gets an answer about those two games and nothing about the other two
hundred things they have open — which is what makes the capability's
wording true rather than a euphemism.

Empty in a browser, and empty on the desktop until somebody lists
something. Both look the same from here on purpose: a plugin should
degrade rather than tell people to go and configure Gryt.

#### `on()`

```ts
on(handler: (running: string[]) => void): () => void
```

Hear when that answer changes. Needs `processes`.

### `gryt.ui`

#### `panel()`

```ts
panel(panel: { title: string; rows: { label: string; value?: string }[] }): Promise<unknown>
```

Needs `display`.

Draw a panel beside the member list.

A title and rows of text, and nothing else — no markup, no colours, no
node handed over. The app renders it with its own components, which is
what lets a plugin be seen at all from inside a worker with no DOM.

Calling it again replaces what is there. There is one panel per plugin,
so a plugin does not have to track handles or clean up more than one.

#### `clear()`

```ts
clear(): Promise<unknown>
```

Needs `display`.

Take it down.

### `gryt.log`

#### `info()`

```ts
info(message: string)
```

#### `warn()`

```ts
warn(message: string)
```

#### `error()`

```ts
error(message: string)
```
