# Panels
A panel is a small HTML app that Offsider shows in a window beside your work: a checklist
to get through while you work, a queue of pull requests to test, anything a web page can show.
Claude Code opens it, follows what you do in it, and hands it new data as things change.
Offsider hosts the interface and its declared tools. It has no recording or drawing engine.
This section covers:
- [panel.json](/docs/panels/manifest.md): the window's size and style, what the app may load, and its
tools.
- [The JavaScript API](/docs/panels/api.md): how the app talks to Offsider, and the MCP Apps messages
underneath.
- [Tools](/docs/panels/tools.md): how a panel gets live data.
- [Headers and backgrounds](/docs/panels/titlebar.md): drawing behind the titlebar.
- [What a panel can't do](/docs/panels/security.md): the sandbox.
## Make one
```sh
offsider init my-panel
offsider open my-panel
```
`init` writes a folder with two files:
- `index.html` — the app. One file is plenty; add scripts, styles and images next to it if you
like.
- `panel.json` — the window's size and style, and the commands the app may run. Optional: without
it, you get a 380×560 window.
`open` asks you to approve all declared tools before loading a panel that has tools.
After approval, it shows the window and streams panel events. Press Ctrl-C to detach while leaving
the panel open; `offsider attach my-panel` resumes the feed and replays recent missed events.
New panels come to the front. Open Panels in Offsider's menu bar or Window menu brings any panel
forward. Command-W minimizes a panel to the Dock without stopping it; `offsider show my-panel`
restores it. Close the panel with its close button or `offsider close my-panel`.
## What `offsider open` prints
Everything that happens in the panel comes out on stdout, one JSON object a line, so Claude Code
can run it in the background and read along:
```json
{"type":"opened","panel":"my-panel"}
{"type":"event","panel":"my-panel","name":"checked","data":{"id":"login"},"note":"Ticked: Log in works"}
{"type":"message","panel":"my-panel","text":"Done with this one."}
{"type":"log","panel":"my-panel","level":"error","data":["TypeError: …"]}
{"type":"closed","panel":"my-panel"}
```
| Type | When |
| --- | --- |
| `opened` | The window is up. |
| `event` | The app called `offsider.emit(name, data, note)`. |
| `message` | The app called `offsider.message(text)`: something meant for whoever opened it. |
| `context` | The app called `offsider.modelContext(text, data)`. |
| `log` | The app logged with `console.error`, `console.warn` or `console.info`, or hit an uncaught error. |
| `closed` | The window closed. The command exits 0 right after. |
The command exits 1 if Offsider quits underneath it.
## Commands
Run `offsider` for a welcome banner and command list. Use `offsider COMMAND --help` for
command-specific usage.
```
offsider init DIR write a starter panel
offsider open DIR [--name NAME] [--input JSON|@FILE]
offsider attach NAME [--after SEQUENCE] resume panel events
offsider send NAME JSON|@FILE hand the open panel new data
offsider show NAME show a hidden panel
offsider close NAME
offsider list
offsider docs print these docs
offsider --help show help (-h)
offsider --version show the installed version (-V)
```
- `--name` defaults to the folder's name. Only one panel can have a given name at a time.
- `--input` is the app's starting data. `@FILE` reads it from a file, and `@-` from stdin.
- `send` delivers new data to an open panel. The app decides what to do with it: replace
everything, or merge.
## With an AI agent
Keep `offsider open` running in a background terminal. Read its JSON lines, act on events,
and send new data with `offsider send`. Tool commands are explicit capabilities declared in
`panel.json`; only open folders whose HTML and commands you trust.
See [AI agent workflows](../../integration.md) for orchestrating panels and tools.