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

# Frontend API

> The myrax object passed to register()

The panel imports your `[ui] entry` as an ES module and calls its exported
`register` function with a per-plugin API object. The same object is also
available globally as `window.myrax[<plugin id>]`.

```js web/plugin.js theme={null}
export async function register(myrax) {
  // myrax.id      — your plugin id
  // myrax.plugin  — the full manifest as the panel sees it
}
```

`register` may be async — the panel awaits it. If it throws, the panel shows
a "Plugin failed" toast with the error message and keeps running.

## sidebar

```js theme={null}
myrax.sidebar.add({
  value: 'main',                  // page id, unique within your plugin
  label: 'My Plugin',             // sidebar text
  icon: '<svg ...>',              // see icon formats below
  mount(node) {                   // called when the user opens the tab
    node.innerHTML = '<h2>hi</h2>';
    return () => { /* cleanup: listeners, timers, sockets */ };
  }
});

myrax.sidebar.remove('processes'); // hide a built-in tab by its value
```

A page renders through exactly one of:

| Key           | Meaning                                                              |
| ------------- | -------------------------------------------------------------------- |
| `mount(node)` | full control — you get an empty container, return a cleanup function |
| `html`        | static HTML string                                                   |
| `render()`    | function returning an HTML string                                    |

`myrax.routes.add(route)` takes the same object — use it for pages that should
exist without a sidebar entry, then navigate to them yourself.

### Icon formats

| Value                              | Treated as                                                                               |
| ---------------------------------- | ---------------------------------------------------------------------------------------- |
| `<svg ...>`                        | inline SVG — use `stroke="currentColor"` / `fill="currentColor"` so it follows the theme |
| `https://...`, `data:...`, `/path` | image URL as-is                                                                          |
| `icons/mark.svg`                   | file inside your plugin, served from `/addons/{id}/`                                     |
| empty                              | the default plug icon                                                                    |

## toast

```js theme={null}
myrax.toast({
  title: "Saved",
  message: "Settings updated successfully",
  tone: "success",    // "success" | "error" | "info"
  timeout: 3000       // ms
});
```

## navigate

```js theme={null}
myrax.navigate('main');   // open one of your pages by its value
```

Values are namespaced internally as `plugin:<id>:<value>` — pass either form.

## styles

Stylesheets listed in the manifest load automatically; these are for dynamic
cases:

```js theme={null}
myrax.styles.inject('.my-thing { color: var(--text); }'); // raw CSS
myrax.styles.load('web/extra.css');                       // file from your plugin
```

Relative paths are served from `/addons/{id}/` with the same `?v=` cache
busting as your entry. Everything you inject is tagged with
`data-myrax-plugin="<id>"` and removed when the plugin is disabled.

## layout

Shorthands over `styles.inject` for hiding panel chrome:

```js theme={null}
myrax.layout.hide('.some-panel-selector');
myrax.layout.show('.some-panel-selector');
```

## refresh

```js theme={null}
await myrax.refresh();   // re-fetch the panel's core data (config, plugins, stats)
```

## Talking to your backend

The browser never reaches your runtime directly — go through the panel's
authenticated proxy:

```js theme={null}
// HTTP → your runtime's /api/status
const res = await fetch(`/api/plugins/${myrax.id}/proxy/api/status`);

// WebSocket → your runtime's /stream
const ws = new WebSocket(
  `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}` +
  `/api/plugins/${myrax.id}/ws/stream`
);
```

Same origin, same session cookie — no CORS, no extra auth.

## Theme tokens

Plugin DOM lives inside the panel, so its CSS variables are yours:
`--bg`, `--panel`, `--panel-2`, `--text`, `--muted`, `--line` and the rest.
Build on them and both themes work without extra code.
