- config.md: add server.guestIdent, report.service, defaults.lang; correct the turnstile.enabled / features.* descriptions; add a Languages section; complete example. - branding.md: branding.name/icon now drive the favicon, notifications, PWA and push; fix the feature-flag notes; add a Languages note. - plugins.md: document orbit.config() and orbit.i18n (language/t/pick) + a Localization section; note they arrived in apiVersion 4. - overview.md: add the 10-language support bullet.
5.2 KiB
Section: Plugins Order: 200
Plugin system
Orbit has a small, operator-controlled plugin system. A deployment lists
plugin scripts in config.json; each is loaded at startup and registers against
a global Orbit object to hook events, read state, send IRC, theme the UI, and
add UI in predefined slots.
Experimental. Orbit is a work in progress and this API may change between releases. Plugins are deployment-controlled — same trust level as the app itself. There is no user-uploaded plugin marketplace, by design.
Enabling plugins
Add script URLs to plugins in config.json:
{ "plugins": ["/app/plugins/orbit-demo.js"] }
They load in order, after the app boots. Host them anywhere the page can reach (same-origin recommended).
A quick plugin
A plugin is a plain .js file that calls Orbit.plugin(). UI is authored with
the html tagged template (runtime markup, no build step), or with
orbit.h(...) (React.createElement).
Orbit.plugin('my-plugin', (orbit, log) => {
log('loaded, Orbit v' + orbit.version);
orbit.on('message', (m) => log(m.from, 'said', m.text, 'in', m.target));
orbit.addUi('composer_button', () =>
orbit.html`<button class="composer__emoji" title="Shrug"
onClick=${() => orbit.irc.say('¯\\_(ツ)_/¯')}>🤷</button>`);
});
For anything substantial — real components with state — build a compiled plugin instead. See Compiled plugins.
The Orbit API
Global
| Member | Description |
|---|---|
Orbit.version / Orbit.commit |
app version + git commit (build-time) |
Orbit.apiVersion |
plugin API contract version (bumped on breaking changes) — guard with if (Orbit.apiVersion < N) … |
Orbit.plugin(name, fn) |
register a plugin; fn(orbit, log) |
Orbit.on/once/off/emit(event, …) |
the app event bus |
Orbit.config() |
the resolved runtime config |
Orbit.React / Orbit.ReactDOM / Orbit.jsxRuntime / Orbit.Fragment / Orbit.h / Orbit.html |
render primitives (externalization targets for compiled plugins) |
Inside Orbit.plugin(name, (orbit) => …)
| Member | Description |
|---|---|
orbit.on/once/off/emit |
event bus (see events below) |
orbit.state.active() |
active buffer name |
orbit.state.nick() / account() |
your nick / logged-in account |
orbit.state.buffers() |
open buffer names |
orbit.state.get() |
full store snapshot (read-only) |
orbit.irc.send(line) |
send a raw IRC line |
orbit.irc.msg(target, text) |
PRIVMSG a target |
orbit.irc.say(text) |
send to the active buffer |
orbit.irc.join(chan) / part(chan) |
join / part |
orbit.irc.list() |
request the channel list |
orbit.themes.current()/list()/set(id) |
read/set the theme |
orbit.storage.get(key, def)/set(key, val) |
namespaced persistence |
orbit.config() |
the resolved runtime config (branding, features, …) |
orbit.i18n.language() |
current UI language code (e.g. es, pt-BR) |
orbit.i18n.t(key, opts?) |
translate an app locale key (supports {{interpolation}}) |
orbit.i18n.pick(table) |
pick a string from a { lang: text } table by the current language |
orbit.addUi(slot, render) |
add UI to a slot (returns a remover) |
orbit.addSettingsSection({label, icon?, render}) |
add a whole Settings section |
orbit.h / orbit.html |
render helpers |
log(…) |
namespaced console logger |
Events
ready, connected ({nick}), status (connection status string),
buffer.active (buffer name), message ({from, target, text, self}),
raw (the parsed IRC message).
UI slots
| Slot | Where |
|---|---|
composer_button |
a button in the message composer toolbar |
settings_section |
a whole section in Settings (own nav entry + pane) — use orbit.addSettingsSection() |
More slots (message decorators, side panels) will be added as the core grows stable homes for them.
Localization (i18n)
The UI ships in 10 languages — keep your plugin in step so it isn't stuck in one. Two ways:
-
Self-contained — carry a
{ lang: text }table and let Orbit pick the current language:const LABEL = { en: 'Copy', fr: 'Copier', de: 'Kopieren' }; orbit.html`<button title=${orbit.i18n.pick(LABEL)}>⧉</button>`; -
Interpolated —
orbit.i18n.t('key', { name })resolves an app locale key with{{placeholders}}, so word order is correct per language. The four bundled plugins (clock, copy, invite, games) use this.
Read the string at render time, not once at load: plugin UI re-renders when the language
changes, so the text follows automatically. orbit.i18n.language() returns the current code
if you need to branch.
orbit.i18nandorbit.config()were added inapiVersion4 — guard withif (orbit.apiVersion >= 4)if your plugin must run on older builds.
Trust & security
Plugins are operator-controlled: a deployment lists them in config.json, so
they run with the same trust as the app itself. There is no user-uploaded plugin
mechanism. Orbit deliberately does not expose internal modules or runtime
component replacement — that would couple plugins to internals that are still
moving. The API above is the stable surface.