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
6 changes: 6 additions & 0 deletions .changeset/plugin-settings-schema-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"emdash": minor
"@emdash-cms/admin": minor
---

Adds the auto-generated admin settings form for plugins that declare `admin.settingsSchema`. A gear icon on the plugin's card in Plugins opens a form generated from the schema (string, number, boolean, select, secret, url, and email fields), persisted to the plugin's KV store under `settings:` keys. Secret fields are write-only: the admin shows whether a value is set but never returns it. Editing plugin settings requires the `plugins:manage` permission.
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ admin: {

Field types: `string`, `number`, `boolean`, `select`, `secret`, `url`, `email`. Each accepts `label`, `description`, `default`, plus type-specific extras like `min`/`max`/`options`. Settings are persisted to the same per-plugin KV store sandboxed plugins use — read them with `ctx.kv.get<T>("settings:<key>")` from anywhere.

The generated form appears behind the gear icon on the plugin's card in **Plugins** (admins only — editing plugin settings requires the `plugins:manage` permission). Secret fields are write-only: the admin never sees the stored value, only whether one is set.

For richer settings UI than `settingsSchema` provides, ship custom React pages — see [React admin pages and widgets](/plugins/creating-native-plugins/react-admin/).

## Complete example — audit log plugin
Expand Down
15 changes: 13 additions & 2 deletions packages/admin/src/components/PluginManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,25 @@ function PluginCard({
</RouterLinkButton>
)}

{plugin.hasSettings && plugin.enabled && (
<RouterLinkButton
to="/plugins-manager/$pluginId/settings"
params={{ pluginId: plugin.id }}
aria-label={t`Settings`}
variant="ghost"
shape="square"
icon={<Gear />}
/>
)}

{plugin.hasAdminPages && plugin.enabled && (
<RouterLinkButton
to="/plugins/$pluginId/$"
params={{ pluginId: plugin.id, _splat: "" }}
aria-label={t`Settings`}
aria-label={t`Plugin pages`}
variant="ghost"
shape="square"
icon={<Gear />}
icon={<FileText />}
/>
)}

Expand Down
Loading
Loading