Documentation Index
Fetch the complete documentation index at: https://docs.radarboard.app/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Plugins add interactive features to Radarboard — task managers, note-taking, bookmarks, RSS readers, and more. Each plugin lives in its own package under plugins/ and provides:
- An overlay UI — side panel, modal, fullscreen, or mini-HUD
- Scoped storage — key-value DB for plugin data
- MCP tools (optional) — AI assistant actions
- Dashboard widgets (optional) — embed data in the grid
- Cross-plugin communication (optional) — intents and RPC
Prerequisites
- Radarboard dev environment set up (Setup Guide)
- Familiarity with React and TypeScript
Step 1: Scaffold
pnpm create-plugin my-plugin
This creates plugins/my-plugin/ with a working overlay, registers it in radarboard.config.ts, and runs pnpm install.
Step 2: Build the Overlay
Edit src/components/my-plugin-overlay.tsx. The component receives api: PluginAPI:
"use client";
import type { PluginRenderProps } from "@radarboard/plugin-sdk/types";
import { useEffect, useState } from "react";
export function MyPluginOverlay({ api }: PluginRenderProps) {
const [items, setItems] = useState([]);
// Load data from the plugin's scoped DB
useEffect(() => {
api.db.list("item:").then(setItems);
}, [api.db]);
// Add a new item
const addItem = async () => {
const item = { id: crypto.randomUUID(), title: "New item" };
await api.db.set(`item:${item.id}`, item);
setItems((prev) => [...prev, item]);
api.notify("Item created!", "success");
};
return (
<div className="p-4">
<button type="button" onClick={addItem}>Add Item</button>
{items.map((item) => (
<div key={item.id}>{item.title}</div>
))}
</div>
);
}
PluginAPI Reference
| Method | Description |
|---|
api.db.get(key) | Read a value from plugin storage |
api.db.set(key, value) | Write a value to plugin storage |
api.db.delete(key) | Remove a value |
api.db.list(prefix) | List all values with a key prefix |
api.notify(msg, type?) | Show a toast ("info", "success", "error") |
api.hotkeys.register(key, fn) | Register a keyboard shortcut (auto-cleanup) |
api.close() | Close the plugin overlay |
api.events.emit(event) | Emit a notification event |
api.events.on(filter, handler) | Subscribe to events |
api.intents.sendTo(plugin, action, payload) | Send data to another plugin |
api.intents.sendToAssistant(payload) | Send data to the AI chat |
api.rpc.call(plugin, action, params) | Call a service on another plugin |
api.projects.list() | Get the project list |
Edit src/index.ts:
export const myPluginDescriptor: PluginDescriptor = {
id: "my-plugin",
name: "My Plugin",
description: "A brief description of what this plugin does.", // max 120 chars
icon: Puzzle,
category: "productivity", // productivity | monitoring | data
version: "0.1.0",
launchSurfaces: ["palette", "topbar"], // where users can open it
presentation: {
default: "side-panel", // side-panel | fullscreen | modal | mini-hud
alternates: ["fullscreen"], // optional alternate modes
size: "md", // sm | md | lg
},
component: MyPluginOverlay,
mcpTools: myPluginMcpTools,
settings: [],
};
Presentation Modes
| Mode | Behavior |
|---|
side-panel | Slides in from the right |
fullscreen | Takes over the viewport |
modal | Centered dialog |
mini-hud | Small floating widget |
Step 4: Add Settings (Optional)
Let users configure your plugin without editing code:
settings: [
{
key: "maxItems",
label: "Maximum Items",
description: "How many items to display at once.",
type: "number",
defaultValue: 50,
},
{
key: "sortOrder",
label: "Sort Order",
type: "select",
defaultValue: "newest",
options: [
{ label: "Newest first", value: "newest" },
{ label: "Oldest first", value: "oldest" },
],
},
],
Read settings in your component:
const maxItems = await api.db.get<number>("_config:maxItems") ?? 50;
Step 5: Test
Run conformance tests (included in the scaffold):
pnpm --filter @radarboard/plugin-my-plugin test
For unit testing with the mock API:
import { createMockPluginAPI } from "@radarboard/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
describe("my-plugin", () => {
it("stores items in db", async () => {
const api = createMockPluginAPI("my-plugin");
await api.db.set("item:1", { id: "1", title: "Test" });
const item = await api.db.get("item:1");
expect(item).toEqual({ id: "1", title: "Test" });
});
it("tracks notifications", () => {
const api = createMockPluginAPI("my-plugin");
api.notify("Hello!", "success");
expect(api.notifications).toEqual([{ message: "Hello!", type: "success" }]);
});
});
Let the AI assistant interact with your plugin:
import { z } from "zod";
import type { McpToolDefinition } from "@radarboard/plugin-sdk/types";
export const myPluginMcpTools: McpToolDefinition[] = [
{
name: "list-items",
description: "List all items in My Plugin",
parameters: z.object({
limit: z.number().optional().describe("Max items to return"),
}),
execute: async (params, api) => {
const items = await api.db.list("item:");
return { items: items.slice(0, params.limit ?? 100) };
},
},
];
Plugins can embed widgets in the dashboard grid:
widgets: [
{
widgetId: "summary",
name: "My Plugin Summary",
description: "Quick overview of plugin data",
defaultSlot: "slot7",
templateConfig: { /* WidgetTemplateConfig */ },
},
],
Optional: Cross-Plugin Communication
Intents (receive data from other plugins)
intents: [
{
action: "add-item",
label: "Add to My Plugin",
accepts: ["text", "url"],
handle: async (payload, api) => {
await api.db.set(`item:${Date.now()}`, { title: payload.text });
return { ok: true };
},
},
],
Services (expose RPC methods)
services: [
{
action: "get-count",
description: "Get total item count",
parameters: z.object({}),
handler: async (_params, api) => {
const items = await api.db.list("item:");
return { count: items.length };
},
},
],
Module Boundaries
Plugins can only import from:
@radarboard/plugin-sdk
@radarboard/types
@radarboard/utils
@radarboard/ui
@radarboard/widget-engine
@radarboard/embedding-service
@radarboard/llm
Reference
- Full type reference:
@radarboard/plugin-sdk/types
- UI components:
@radarboard/plugin-sdk/components/*
- Testing utilities:
@radarboard/plugin-sdk/testing
- Real examples:
plugins/tasks/, plugins/notes/, plugins/bookmarks/
- Extension rules:
CONTRIBUTING-EXTENSIONS.md