Add orbit.server plugin API: read-only caps/isupport/network/numeric
This commit is contained in:
parent
d24833e213
commit
e302e38d24
2 changed files with 36 additions and 8 deletions
|
|
@ -54,7 +54,7 @@ bound to the app's React) — runtime template markup, no build step. Prefer
|
|||
| 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.apiVersion` | plugin API contract version (bumped on surface changes) — feature-detect 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 |
|
||||
|
|
@ -68,6 +68,11 @@ bound to the app's React) — runtime template markup, no build step. Prefer
|
|||
| `orbit.state.nick()` / `account()` | your nick / logged-in account |
|
||||
| `orbit.state.buffers()` | open buffer names |
|
||||
| `orbit.state.get()` | full store snapshot (read-only) |
|
||||
| `orbit.server.hasCap(cap)` | is an IRCv3 capability negotiated? — gate cap-dependent features so you never 421 a leaner server |
|
||||
| `orbit.server.caps()` | every cap Orbit negotiates, each `{name, available, enabled}` |
|
||||
| `orbit.server.isupport()` | read-only snapshot of the ISUPPORT (005) tokens |
|
||||
| `orbit.server.network()` | network name (ISUPPORT NETWORK) |
|
||||
| `orbit.server.numeric(code)` | RPL/ERR name for a numeric reply, e.g. `'433'` → `'ERR_NICKNAMEINUSE'` |
|
||||
| `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 |
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// The `window.Orbit` plugin API — the surface plugins register against.
|
||||
//
|
||||
// We deliberately expose only safe, stable capabilities (events, read-only
|
||||
// state, IRC actions, theming, namespaced storage, named UI slots) and keep
|
||||
// internal modules private so the app's still-evolving internals stay free to
|
||||
// change. UI authoring uses HTM (htm.bind to the app's React) so plugins write
|
||||
// template-literal markup at runtime, with no build step.
|
||||
// state, read-only server/capability info, IRC actions, theming, namespaced
|
||||
// storage, named UI slots) and keep internal modules private so the app's
|
||||
// still-evolving internals stay free to change. UI authoring uses HTM (htm.bind
|
||||
// to the app's React) so plugins write template-literal markup at runtime, with
|
||||
// no build step.
|
||||
import React, { type ReactNode } from 'react';
|
||||
import * as ReactJSXRuntime from 'react/jsx-runtime';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
|
@ -21,9 +22,10 @@ import { usePluginRegistry, type UiSlot, type MessageInfo, type UserActionCtx, t
|
|||
const html = htm.bind(React.createElement);
|
||||
const THEMES: Theme[] = ['light', 'dark', 'orbit', 'orbit-dark', 'yomirc', 'yomirc-dark'];
|
||||
|
||||
// Plugin API contract version. Bumped on a breaking change to the surface below
|
||||
// so plugins can guard (e.g. `if (Orbit.apiVersion < 2) …`). Still experimental.
|
||||
const API_VERSION = 5;
|
||||
// Plugin API contract version. Bumped on any change to the surface below so
|
||||
// plugins can feature-detect (e.g. `if (Orbit.apiVersion >= 6) orbit.server.hasCap(…)`).
|
||||
// Still experimental.
|
||||
const API_VERSION = 6;
|
||||
|
||||
const registered = new Map<string, OrbitPluginApi>();
|
||||
|
||||
|
|
@ -59,6 +61,20 @@ export interface OrbitPluginApi {
|
|||
buffers: () => string[];
|
||||
get: () => ReturnType<typeof useChat.getState>;
|
||||
};
|
||||
// ── read-only server / capability info (client.server + ircv3 + numerics) ──
|
||||
server: {
|
||||
/** Network name (ISUPPORT NETWORK), '' until known. */
|
||||
network: () => string;
|
||||
/** Read-only snapshot of the server's ISUPPORT (005) tokens. */
|
||||
isupport: () => Record<string, string>;
|
||||
/** True once the given IRCv3 capability is negotiated and in use — check this
|
||||
* before using a cap-dependent feature so you never 421 a leaner server. */
|
||||
hasCap: (cap: string) => boolean;
|
||||
/** Every cap Orbit negotiates + whether the server advertises / enabled it. */
|
||||
caps: () => { name: string; available: boolean; enabled: boolean }[];
|
||||
/** RPL/ERR name for a numeric reply seen via on('raw'), e.g. '433' → 'ERR_NICKNAMEINUSE'. */
|
||||
numeric: (code: string) => string | undefined;
|
||||
};
|
||||
// ── IRC actions ──────────────────────────────────────────────────────────
|
||||
irc: {
|
||||
send: (line: string) => void;
|
||||
|
|
@ -134,6 +150,13 @@ function makeApi(name: string): OrbitPluginApi {
|
|||
// through the curated irc.* methods; they never get a credential handle.
|
||||
get: () => ({ ...activeStore().getState(), client: null }),
|
||||
},
|
||||
server: {
|
||||
network: () => activeStore().getState().client?.server.network ?? '',
|
||||
isupport: () => ({ ...(activeStore().getState().client?.server.isupport ?? {}) }),
|
||||
hasCap: (cap) => activeStore().getState().client?.ircv3.hasCap(cap) ?? false,
|
||||
caps: () => activeStore().getState().client?.ircv3.listCaps() ?? [],
|
||||
numeric: (code) => activeStore().getState().client?.numerics.name(code),
|
||||
},
|
||||
irc: {
|
||||
send: (line) => activeStore().getState().client?.send(line),
|
||||
msg: (target, text) => activeStore().getState().client?.privmsg(target, text),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue