@tinstar/plugin-api
v5.1.0
Published
Public API types for building Tinstar plugins
Downloads
152
Maintainers
Readme
@tinstar/plugin-api
Public API types for building Tinstar plugins.
What is a Tinstar plugin?
A plugin is an npm package (or a local folder) that contributes widgets,
panes, commands, or other extension points to the Tinstar canvas. Plugins
run in-process with the host — they have full access to @tinstar/plugin-api
and React.
Installation
npm install --save-dev @tinstar/plugin-api react
# Then in your package.json, add:
# "peerDependencies": { "@tinstar/plugin-api": "^5.0.0", "react": "^18 || ^19" }Minimal plugin
// src/tinstar-plugin.tsx
import { definePlugin, TINSTAR_API_VERSION } from '@tinstar/plugin-api'
import type { WidgetProps } from '@tinstar/plugin-api'
function HelloWidget({ data }: WidgetProps) {
const name = (data as { name?: string }).name ?? 'world'
return <div>hello {name}</div>
}
export default definePlugin({
activate(api) {
api.logger.info(`hello plugin activating against api v${TINSTAR_API_VERSION}`)
return [
api.widgets.register({
type: 'hello-widget',
component: HelloWidget,
isContainer: false,
defaultSize: { width: 320, height: 200 },
minSize: { width: 200, height: 120 },
}),
]
},
})Add a tinstar block to your package.json:
{
"name": "your-plugin",
"version": "0.1.0",
"main": "dist/tinstar-plugin.js",
"tinstar": {
"apiVersion": "5",
"displayName": "Hello plugin",
"contributes": {
"widgets": [{ "type": "hello-widget", "label": "Hello", "defaultSize": { "width": 320, "height": 200 } }]
}
}
}Loading your plugin
Add to ~/.config/tinstar/plugins.json:
{
"disabled": [],
"external": [
{ "name": "your-plugin", "path": "/absolute/path/to/your-plugin" }
]
}Then restart Tinstar. The Settings → Plugins tab will list your plugin.
API surface (v5.0)
api.widgets.register(reg): Disposable— register a canvas widget type.api.http.fetch(path, init?): Promise<Response>— wraps tinstar's auth-aware fetch; auto-addsX-Tinstar-Pluginheader.api.events.subscribe(channel, handler): Disposable— subscribe to host SSE events.api.logger.{debug,info,warn,error}— plugin-id-prefixed logger.api.pluginId,api.version— identity fields.api.canvas.fitWidget(widgetId)— zoom/pan the canvas to frame a specific widget.api.hotkeys.onAction(widgetId, handler): Disposable— receive hotkey action strings (e.g.'fit-viewport') when this widget has focus.api.theme.accent.{resolve,hexToRgba}— normalize and alpha-blend accent colors consistent with host chrome.api.watch.file(sessionId, filePath)— React hook: live file content from the host's file-watcher SSE channel.api.watch.image(sessionId, filePath)— React hook: image-change notifications.api.constellations— peer discovery, capability publish/invoke, slot membership, and arrange actions. See constellations & capabilities.
Known SSE event channels
The host emits these SSE event names (subscribe by exact name, no wildcards in v5.0):
snapshot,delta— workspace statenats_traffic— NATS messagesfile_watch— file system changestelemetry:hud,canvas:viewport— UI telemetryprojects_changed,ready_queue_update,heartbeat
Versioning
apiVersion: "5" is a hard handshake. The host rejects plugins built against
a different major. Additive changes (new hooks, new event channels) do not
bump.
Building your plugin
The host externalizes @tinstar/plugin-api and react at runtime via an
importmap. Your build must NOT bundle them. With esbuild:
esbuild src/tinstar-plugin.tsx --bundle --format=esm --platform=browser \
--external:@tinstar/plugin-api --external:react --external:react-dom \
--outfile=dist/tinstar-plugin.jsConstellations & capabilities
A constellation is a cluster of widgets that move together, share a slot key (1–9), and can discover and invoke each other via capability-based RPC. See the full guide: docs/plugins/constellations-and-capabilities.md.
Migrating from V4
api.hotgroups → api.constellations
The api.hotgroups surface is renamed to api.constellations. It is a search-and-replace fix at every call site:
// before
api.hotgroups.useContext()
api.hotgroups.Badge
// after
api.constellations.useContext()
api.constellations.Badgeapi.constellations.Badge and api.constellations.useContext() keep the same signatures — the rename is backward-compatible at the type level.
New surfaces on api.constellations
The following hooks were added in V5.0 and have no V4 equivalent:
useMyNodeId()— returns this widget's full host node iduseMySlots()— returns the slot keys ('1'..'9') this widget belongs touseMySlot()— returns the primary slot as a number (1–9), or nullusePeers()— returnsConstellationPeer[]for all peers in the same constellationusePublishCapability()— returns apublish(name, handler)functionuseInvokePeerCapability()— returns aninvoke(peerId, name, args)functionuseFitToMine()— returns afit()callback that frames this widget's constellationuseTidyMine()— returns atidy()callback that grid-arranges the constellationuseAssignToSlot()— returns anassign(slot)callbackuseLeave()— returns aleave()callback that removes this widget from its constellation
apiVersion bump: "4" → "5"
Change the apiVersion field in your package.json tinstar block:
{ "tinstar": { "apiVersion": "5" } }The host hard-rejects plugins with a mismatched major. Rebuild and reload after updating the manifest.
Storage key change
The host's internal constellation storage key changed from tinstar-hotgroups-v2-<spaceId> to tinstar-constellations-v1-<spaceId>. Plugins should never read this key directly — it is host-internal. If you were reading it (you should not have been), migrate to api.constellations.useMySlots() / usePeers().
License
MIT — see the tinstar repo.
