feat(plugins): render plugin-settings slot at top of settings page (#1838)

Move the plugin-settings slot above the schema-driven settings card so a
plugin's live status/health card is the first content under the header,
instead of sitting between the settings and manifest cards. Docs updated to
match.

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
João Salavisa
2026-07-21 14:07:54 +01:00
committed by GitHub
parent 8f2fe7a8e6
commit 2921553d40
5 changed files with 13 additions and 13 deletions
@@ -54,13 +54,13 @@ export function PluginDetail({ pluginId }: { pluginId: string }) {
<PluginDetailHeader plugin={plugin} />
<Separator />
<PluginSettingsCard plugin={plugin} form={form} busy={actions.busyId === plugin.id} />
{/* Owner-scoped inline slot for the plugin's own settings UI (see PLUGIN-API.md). */}
{/* Owner-scoped inline slot for the plugin's own settings UI, at the top (see PLUGIN-API.md). */}
<PluginSlot
name="plugin-settings"
ownerPluginId={plugin.id}
slotProps={{ pluginId: plugin.id, status: plugin.status }}
/>
<PluginSettingsCard plugin={plugin} form={form} busy={actions.busyId === plugin.id} />
<PluginManifestCard plugin={plugin} />
<PluginDangerZone plugin={plugin} actions={actions} />
+2 -2
View File
@@ -77,8 +77,8 @@ export interface PluginRouteOptions {
* `slotProps`), "chat-top-bar" (status in the session top bar, beside the
* CPU/DB metrics — receives `{ taskId, taskTitle, workspaceId, activeSessionId,
* sessionIds }`), and "plugin-settings" (inline UI on a plugin's own settings
* page, Settings > Plugins > <plugin>, between the settings form and the
* manifest card — receives `{ pluginId: string; status: PluginStatus }` as
* page, Settings > Plugins > <plugin>, at the top above the settings form —
* receives `{ pluginId: string; status: PluginStatus }` as
* `slotProps`). "plugin-settings" is owner-scoped: the host renders only the
* component registered by the plugin being viewed, so a plugin never appears on
* another plugin's page and authors don't gate on the current id themselves.
+2 -2
View File
@@ -136,8 +136,8 @@ interface PluginRegistry {
// tokscale cost data on a session) is the plugin's job, done server-side in
// the plugin backend via the Host data API; the host only propagates ids.
// "plugin-settings" renders inline on a plugin's own settings page
// (Settings > Plugins > <plugin>, between the schema-driven settings form and
// the manifest card) and forwards `{ pluginId: string, status: PluginStatus }`
// (Settings > Plugins > <plugin>, at the top above the schema-driven settings
// form) and forwards `{ pluginId: string, status: PluginStatus }`
// as `slotProps`. It is owner-scoped: the host renders only the component
// registered by the plugin currently being viewed, so your card appears on
// your own settings page and never on another plugin's — no per-id gating
+5 -5
View File
@@ -344,7 +344,7 @@ plugins at once. Available slots:
| `main-nav-footer` | Footer of the main sidebar | — |
| `chat-input-actions` | Chat composer toolbar, beside the model picker, mic, and send button | `{ taskId, taskTitle, activeSessionId, sessionIds }` |
| `chat-top-bar` | Session top bar, beside the CPU/DB metrics and the document/editor/debug controls | `{ taskId, taskTitle, workspaceId, activeSessionId, sessionIds }` |
| `plugin-settings` | A plugin's own settings page (**Settings > Plugins > `<plugin>`**), between the settings form and the manifest card | `{ pluginId, status }` |
| `plugin-settings` | A plugin's own settings page (**Settings > Plugins > `<plugin>`**), at the top above the settings form | `{ pluginId, status }` |
`plugin-settings` is the one exception to "every plugin's component renders":
it is **owner-scoped**, so the host renders only the component registered by the
@@ -444,10 +444,10 @@ registry.registerComponent("chat-top-bar", makeTopBarStatus(host));
### Plugin settings page
Register a `plugin-settings` component to render your own UI inline on your
plugin's settings page (**Settings > Plugins > `<plugin>`**), between the
schema-driven settings form and the manifest card. Use it for live integration
health — e.g. "CLI installed ✅ v0.45.2" or "API token ✅ authenticated" — or any
custom controls next to the config form. The host passes:
plugin's settings page (**Settings > Plugins > `<plugin>`**), at the top above
the schema-driven settings form. Use it for live integration health — e.g. "CLI
installed ✅ v0.45.2" or "API token ✅ authenticated" — or any custom controls
alongside the config form. The host passes:
```ts
type PluginSettingsSlotProps = {
+2 -2
View File
@@ -143,8 +143,8 @@ references rather than cleartext for secret fields.
![A plugin's settings page: a schema-driven form with a masked API-token field, a toggle, and a text field, above a Manifest card showing the plugin's id, version, signing status, and capabilities.](../screenshots/plugin-settings-page.png)
A plugin can also render its own UI inline on this page — between the settings
form and the manifest card — via the `plugin-settings` slot, for example a live
A plugin can also render its own UI inline on this page — at the top, above the
settings form — via the `plugin-settings` slot, for example a live
integration-health card ("CLI installed ✅ v0.45.2", "API token ✅
authenticated"). This is owner-scoped, so a plugin's card only ever appears on
its own settings page. See [Named