orbit-client-docs/docs/compiled-plugins.md

103 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Section: Plugins
Order: 210
# Compiled plugins
A quick `.js` plugin (see [Plugin system](/docs/plugins/)) is great for small
things. For anything substantial — real components with state and hooks — build
the plugin like a normal project and compile it down to **one droppable file**: a
compiled, externalized-React plugin model.
## The trick: externalize React
The output bundle must **not** carry its own copy of React. Two React instances
on one page break hooks ("invalid hook call"). Instead, mark `react`, `react-dom`
and `react/jsx-runtime` **external** and map them to Orbit's globals, so your
plugin shares the host's single React instance:
| Module | Maps to |
|---|---|
| `react` | `Orbit.React` |
| `react-dom` | `Orbit.ReactDOM` |
| `react/jsx-runtime` | `Orbit.jsxRuntime` |
Because your component is created with Orbit's React and rendered through a slot
into Orbit's tree, **hooks, state, context and effects all work**.
## Vite config
```ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
lib: { entry: 'src/index.tsx', formats: ['iife'], name: 'MyOrbitPlugin',
fileName: () => 'my-plugin.js' },
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
output: { globals: {
react: 'Orbit.React',
'react-dom': 'Orbit.ReactDOM',
'react/jsx-runtime': 'Orbit.jsxRuntime',
} },
},
},
});
```
## Write normal TSX
```tsx
import { useState } from 'react';
Orbit.plugin('my-plugin', (orbit) => {
orbit.addUi('composer_button', () => <ShrugButton orbit={orbit} />);
orbit.addSettingsSection({
label: 'My plugin', icon: '🧩',
render: () => <Panel orbit={orbit} />,
});
});
function Panel({ orbit }) {
const [count, setCount] = useState(() => orbit.storage.get('count', 0));
const bump = () => { const n = count + 1; setCount(n); orbit.storage.set('count', n); };
return (
<div className="scard"><div className="scard__body">
<div className="sfield"><div className="sfield__intro">
Compiled plugin real React with hooks. You are <b>{orbit.state.nick()}</b>.
</div></div>
<div className="modal__actions">
<button className="upbtn upbtn--primary" onClick={bump}>Clicked {count}×</button>
</div>
</div></div>
);
}
```
The compiled bundle is tiny — it contains only your code, calling Orbit's React.
## Starter template
A ready-to-copy starter (Vite config with the externals set up, `tsconfig`,
ambient types and a working example) lives in the repo at
[`plugin-template/`](https://codeberg.org/reversefr/orbit/src/branch/main/plugin-template).
```bash
git clone https://codeberg.org/reversefr/orbit.git
cd orbit/plugin-template
npm install
npm run build # → dist/orbit-plugin-template.js
```
## Deploy
1. Copy the built `.js` to where Orbit can fetch it (e.g. `/app/plugins/`).
2. List it in the deployed `config.json` — no rebuild of Orbit needed:
```json
{ "plugins": ["/app/plugins/my-plugin.js"] }
```
3. Reload. Your composer button and Settings section appear.