Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions apps/server/src/services/plugins/builtin-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,6 @@ export const OFFICIAL_PLUGINS = [
defaultEnabled: true,
category: "Workflow management",
},
// Replaces the standard sidebar, so users install it deliberately instead
// of receiving a disabled registration by default.
{
name: "t3sidebar",
pluginId: "t3sidebar",
defaultEnabled: false,
category: "Interface",
},
].map(
(plugin): BundledPluginDefinition => ({
...plugin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ them by mixing ink into canvas), the `--primary` accent, the secondary text tier
(except `side-chat`, which is gated by the **"Side chat plugin"**
experiment); official plugins install from the bundled store on demand.
- **BB Official plugins** (store under `/api/v1/plugin-catalog`):
- BB's official plugins (GitHub, Docs, Memory, Tasks, T3 Sidebar) ship
- BB's official plugins (GitHub, Docs, Memory, and Tasks) ship
bundled inside the app and install from the local copy — no network. Installed official
plugins are pinned to the bundled copy and update with BB app releases.
- `bb plugin search <query> [--json]` — search the official plugins by id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,8 @@ Remaining reference examples in `examples/plugins/`:
`experimental_NewThreadComposer`, plus a thin index backend (kv layout
state, background service + realtime), pure row projection, and a
bare-letter keymap that coexists with a dozen live composers.
- `t3sidebar` — an inbox-style replacement for the sidebar thread list, with
header chips for child threads and plugin-owned settled and snoozed state.

## Gotchas

Expand Down
4 changes: 2 additions & 2 deletions apps/server/test/services/plugins/builtin-plugins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ describe("builtin plugin reconciliation", () => {

it("keeps official plugins bundled but out of the auto-install builtins", () => {
const optionalNames = OFFICIAL_PLUGINS.map((plugin) => plugin.name);
for (const name of ["memory", "t3sidebar"]) {
expect(optionalNames).toEqual(["github", "docs", "memory", "tasks"]);
for (const name of optionalNames) {
expect(BUILTIN_PLUGINS.map((plugin) => plugin.name)).not.toContain(name);
expect(optionalNames).toContain(name);
}
expect(OFFICIAL_PLUGINS.every((plugin) => !plugin.autoInstall)).toBe(true);
});
Expand Down
1 change: 0 additions & 1 deletion apps/server/test/services/plugins/official-plugins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ describe("official plugin registry invariants", () => {
memory: "Context & knowledge",
secrets: "Developer tools",
"side-chat": "Agent interaction",
t3sidebar: "Interface",
tasks: "Workflow management",
workflows: "Workflow management",
};
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ Plugin state lives under the data dir:
commands, injected into agent threads)
```

BB's official plugins (GitHub, Docs, Memory, Tasks, T3 Sidebar) ship bundled
BB's official plugins (GitHub, Docs, Memory, and Tasks) ship bundled
inside the app and install from the local bundled copy — no network, no remote catalog.
Discover them with `bb plugin search` or Extensions → Plugins → Browse; users
cannot add, remove, or configure the official plugin set. Installed official
Expand Down
13 changes: 6 additions & 7 deletions docs/official-plugin-release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ until a user installs them.

The official plugins are:

| Directory | Package name | Store entry | Plugin id |
| ---------------------- | ------------------------ | ----------- | -------------- |
| `plugins/github` | `bb-plugin-github` | `github` | `github` |
| `plugins/docs` | `bb-plugin-simple-notes` | `docs` | `simple-notes` |
| `plugins/memory` | `bb-plugin-memory` | `memory` | `memory` |
| `plugins/tasks` | `bb-plugin-tasks` | `tasks` | `tasks` |
| `plugins/t3sidebar` | `bb-plugin-t3sidebar` | `t3sidebar` | `t3sidebar` |
| Directory | Package name | Store entry | Plugin id |
| ---------------- | ------------------------ | ----------- | -------------- |
| `plugins/github` | `bb-plugin-github` | `github` | `github` |
| `plugins/docs` | `bb-plugin-simple-notes` | `docs` | `simple-notes` |
| `plugins/memory` | `bb-plugin-memory` | `memory` | `memory` |
| `plugins/tasks` | `bb-plugin-tasks` | `tasks` | `tasks` |

## Releasing a change

Expand Down
3 changes: 2 additions & 1 deletion docs/plugin-sidebar-thread-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Status: **implemented**. The members below ship in `@bb/plugin-sdk/app`.

This document specifies one exclusive slot and the data surface it needs.
A plugin uses them to replace bb's thread list with its own. The reference
consumer is the `t3sidebar` plugin in [`plugins/t3sidebar`](../plugins/t3sidebar).
consumer is the `t3sidebar` plugin in
[`examples/plugins/t3sidebar`](../examples/plugins/t3sidebar).

Every member below ships with the `experimental_` prefix and an entry in
[api_to_audit.md](api_to_audit.md), per [AGENTS.md](../AGENTS.md).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
An inbox-style replacement for bb's sidebar thread list, and the reference
example for `app.slots.experimental_threadList`.

This plugin is an example. BB does not bundle it or list it in the official
plugin catalog. Install it from a BB checkout:

```sh
bb plugin install ./examples/plugins/t3sidebar
```

Turn it on in **Settings → Appearance → Sidebar**. bb's own list stays the
default, and comes back the moment you switch away or disable this plugin.

Expand Down Expand Up @@ -31,6 +38,7 @@ Three shelves:
for a failure, the circle-question for a raised hand, the spinner for live
work, and a blue notification dot for a thread that finished while you were
not looking. Both lists sit in the same window, so they speak one language.

- **Snoozed** — hidden until a wake time you chose. A snoozed thread comes
back early if it starts working or asks you something.
- **Settled** — work you are done with, collapsed to one line each.
Expand All @@ -56,22 +64,24 @@ header shows no parent chip.

## What it demonstrates

| Plugin API | Used for |
| --- | --- |
| `experimental_threadList` | the sidebar's scrolling list (bb keeps the New-thread button, search, nav rows, and footer) |
| `experimental_threadHeaderAction` | the two header chips: children on a parent, and the way back on a child |
| `experimental_useSidebarThreads` | live threads and projects, from the host's own cache |
| `experimental_useSidebarThreadActions` | open, open-in-split, new thread |
| `experimental_useSidebarThreadSplit` | dragging a card out to a split pane |
| `experimental_useSidebarThreadPullRequest` | the `#412` badge, coloured by bb's attention state |
| `@radix-ui/react-context-menu` (shimmed) | this plugin's own right-click menu, built on the action hook |
| `bb.storage.database()` + `bb.rpc` + `bb.realtime` | the settled/snoozed store |
| Plugin API | Used for |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `experimental_threadList` | the sidebar's scrolling list (bb keeps the New-thread button, search, nav rows, and footer) |
| `experimental_threadHeaderAction` | the two header chips: children on a parent, and the way back on a child |
| `experimental_useSidebarThreads` | live threads and projects, from the host's own cache |
| `experimental_useSidebarThreadActions` | open, open-in-split, new thread |
| `experimental_useSidebarThreadSplit` | dragging a card out to a split pane |
| `experimental_useSidebarThreadPullRequest` | the `#412` badge, coloured by bb's attention state |
| `@radix-ui/react-context-menu` (shimmed) | this plugin's own right-click menu, built on the action hook |
| `bb.storage.database()` + `bb.rpc` + `bb.realtime` | the settled/snoozed store |

The plugin API ships **no components**. Status glyphs and the right-click menu
are both this plugin's own: `indicator` arrives as data, and every menu item is
one call on `experimental_useSidebarThreadActions`. Choosing them is the point
of a replaced sidebar. Deletion still routes through `requestDelete`, so BB
shows its confirmation dialog rather than a plugin deleting a subtree silently.
The small icon and select components also live in this example. The example
does not import BB's private shared UI package.

## Where the lifecycle lives

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"type": "module",
"description": "An inbox-style sidebar: one flat list of cards that never re-orders.",
"engines": {
"bb": ">=0.0"
"bb": ">=0.0",
"bbPluginSdk": "^0.4.1"
},
"bb": {
"name": "t3sidebar",
Expand All @@ -24,7 +25,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@bb/shared-ui": "workspace:*",
"@hugeicons/core-free-icons": "^4.1.3",
"@hugeicons/react": "^1.1.6",
"clsx": "^2.1.1",
"tailwind-merge": "^3.4.0",
"zod": "^4.3.6"
},
"devDependencies": {
Expand All @@ -40,6 +44,7 @@
"vitest": "^4.1.1",
"@types/better-sqlite3": "^7.6.12",
"better-sqlite3": "12.10.0",
"@radix-ui/react-context-menu": "^2.2.16"
"@radix-ui/react-context-menu": "^2.3.3",
"@radix-ui/react-select": "^2.3.3"
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import type { PluginSidebarThread } from "@bb/plugin-sdk";
// Load through the harness so the plugin's `@bb/plugin-sdk/app` import binds
// to the test runtime.
const app = await loadPluginApp(() => import("../app"));
const parentChip = app.threadHeaderActions.find((slot) => slot.id === "parent")!;
const parentChip = app.threadHeaderActions.find(
(slot) => slot.id === "parent",
)!;

function thread(
overrides: Partial<PluginSidebarThread> = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
experimental_useSidebarThreads as useSidebarThreads,
type PluginThreadHeaderActionProps,
} from "@bb/plugin-sdk/app";
import { Icon } from "@bb/shared-ui/icon";
import { cn } from "@bb/shared-ui/lib/utils";
import { Icon } from "./components/Icon";
import { cn } from "./lib/utils";
import { Disc } from "./Disc";
import { parentOf, threadDisplayTitle } from "./inbox";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cn } from "@bb/shared-ui/lib/utils";
import { cn } from "./lib/utils";
import { TRAILING_GLYPH_BOX_CLASS } from "./StatusSlot";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
experimental_useSidebarThreadActions as useSidebarThreadActions,
type PluginSidebarThread,
} from "@bb/plugin-sdk/app";
import { cn } from "@bb/shared-ui/lib/utils";
import { cn } from "./lib/utils";

/**
* This sidebar's own right-click menu.
Expand Down Expand Up @@ -36,10 +36,14 @@ export function RowContextMenu({
Open in split
</Item>
<Separator />
<Item onSelect={() => void actions.setRead(thread.id, thread.isUnread)}>
<Item
onSelect={() => void actions.setRead(thread.id, thread.isUnread)}
>
{thread.isUnread ? "Mark read" : "Mark unread"}
</Item>
<Item onSelect={() => void actions.setPinned(thread.id, !thread.isPinned)}>
<Item
onSelect={() => void actions.setPinned(thread.id, !thread.isPinned)}
>
{thread.isPinned ? "Unpin" : "Pin"}
</Item>
<Separator />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {
experimental_useSidebarThreadActions as useSidebarThreadActions,
type PluginSidebarThread,
} from "@bb/plugin-sdk/app";
import { Icon } from "@bb/shared-ui/icon";
import { cn } from "@bb/shared-ui/lib/utils";
import { Icon } from "./components/Icon";
import { cn } from "./lib/utils";
import { RowContextMenu } from "./RowContextMenu";
import { STATUS_SLOT_CLASS, StatusOrTime } from "./StatusSlot";
import { threadDisplayTitle } from "./inbox";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { PluginSidebarThreadIndicator } from "@bb/plugin-sdk";
import { Icon } from "@bb/shared-ui/icon";
import { cn } from "@bb/shared-ui/lib/utils";
import { Icon } from "./components/Icon";
import { cn } from "./lib/utils";

/**
* This plugin's status glyphs, matching bb's own sidebar shape for shape: the
Expand Down Expand Up @@ -60,7 +60,11 @@ export function StatusGlyph({
switch (indicator) {
case "unread-error":
return (
<Icon name="CircleX" aria-label={aria} className={cn(shared, "text-destructive")} />
<Icon
name="CircleX"
aria-label={aria}
className={cn(shared, "text-destructive")}
/>
);
case "waiting-for-input":
return (
Expand Down Expand Up @@ -91,7 +95,11 @@ export function StatusGlyph({
case "draft":
case "working-draft":
return (
<Icon name="Edit" aria-label={aria} className={cn(shared, "text-muted-foreground")} />
<Icon
name="Edit"
aria-label={aria}
className={cn(shared, "text-muted-foreground")}
/>
);
case "unread-success":
// The notification dot, in a box the size of every other glyph, the way
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type PluginSidebarThread,
type PluginThreadHeaderActionProps,
} from "@bb/plugin-sdk/app";
import { cn } from "@bb/shared-ui/lib/utils";
import { cn } from "./lib/utils";
import { Disc } from "./Disc";
import { StatusGlyph } from "./StatusGlyph";
import { childrenOf, threadDisplayTitle } from "./inbox";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
experimental_useSidebarThreadActions as useSidebarThreadActions,
type PluginSidebarThread,
} from "@bb/plugin-sdk/app";
import { Icon } from "@bb/shared-ui/icon";
import { cn } from "@bb/shared-ui/lib/utils";
import { Icon, type IconName } from "./components/Icon";
import { cn } from "./lib/utils";
import { RowContextMenu } from "./RowContextMenu";
import { ProviderGlyph } from "./ProviderGlyph";
import { STATUS_SLOT_CLASS, StatusOrTime } from "./StatusSlot";
Expand Down Expand Up @@ -182,7 +182,7 @@ function ParkButton({
onActivate,
}: {
label: string;
icon: "Clock" | "Check";
icon: Extract<IconName, "Clock" | "Check">;
onActivate: () => void;
}) {
return (
Expand Down
Loading