feat(plugins): add theme contributions

Adds plugin-contributed light and dark app themes, including persistence, host-aware coalescing, fallback behavior, documentation, examples, and browser/iOS QA.
This commit is contained in:
Okan YILDIRIM
2026-08-21 11:48:12 +02:00
committed by GitHub
parent a7f3bf7abb
commit e1805ef542
41 changed files with 1238 additions and 203 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ category: Plugins
See the [plugin roadmap](https://github.com/getpaseo/paseo/labels/plugins) for planned contribution
surfaces and their current status.
Paseo plugins add native workspace panels, Command Center items, global surfaces, daemon behavior, and composer attachment sources. They run on every Paseo client connected to the host, including mobile.
Paseo plugins add native workspace panels, Command Center items, global surfaces, app themes, daemon behavior, and composer attachment sources. They run on every Paseo client connected to the host, including mobile.
Plugins are trusted local code. Install only code you trust: backend code runs unsandboxed with access to the daemon machine, and client contributions run inside the Paseo app.
@@ -153,5 +153,5 @@ security behavior.
## Next
- [Plugin reference](/docs/plugins/reference), add daemon behavior, use the Paseo SDK, contribute attachments, and manage lifecycle.
- [Plugin reference](/docs/plugins/reference), add daemon behavior, use the Paseo SDK, contribute themes and attachments, and manage lifecycle.
- [TypeScript SDK](/docs/sdk), the workspace, agent, provider, and config API exposed inside plugins.
+65 -7
View File
@@ -13,6 +13,7 @@ Local plugins are directory sources installed into one Paseo daemon. A plugin ca
- React Native surfaces and sidebar items to Paseo clients;
- workspace and agent panels opened as workspace tabs;
- global, workspace, and agent actions in the Command Center;
- dark themes in Settings → Appearance;
- schema-validated RPC handlers running beside the daemon;
- normal Paseo operations through the TypeScript SDK;
- searchable external resources in the message composer.
@@ -150,13 +151,16 @@ Plugin UI runs on desktop, browser, iOS, and Android, across every Paseo theme.
Recreate styles when `theme` or `layout.compact` changes.
| Key | Required for | Use it for |
| ------------------------------ | -------------------------- | ----------------------------------- |
| `theme.colors.foreground` | Every primary `Text` | Titles and body copy |
| `theme.colors.foregroundMuted` | Secondary `Text` | Labels and supporting copy |
| `theme.colors.surface0` | Root view | Panel background |
| `layout.compact` | Padding and stacking | `true` on mobile and narrow windows |
| `layout.platform` | Platform-specific behavior | `ios`, `android`, or `web` |
| Key | Required for | Use it for |
| ------------------------------- | -------------------------- | ----------------------------------- |
| `theme.colors.foreground` | Every primary `Text` | Titles and body copy |
| `theme.colors.foregroundMuted` | Secondary `Text` | Labels and supporting copy |
| `theme.colors.surface0` | Root view | Panel background |
| `theme.colors.accent` | Primary action fills | Buttons and selected states |
| `theme.colors.accentForeground` | Text on an accent fill | Button labels |
| `theme.colors.statusDanger` | Failure copy | Error messages and destructive text |
| `layout.compact` | Padding and stacking | `true` on mobile and narrow windows |
| `layout.platform` | Platform-specific behavior | `ios`, `android`, or `web` |
Do not hardcode `#000`, `#fff`, or React Native's default text color. Primary copy uses `foreground`. Labels use `foregroundMuted`. Tighten padding when `layout.compact` is true.
@@ -164,6 +168,60 @@ Workspace and agent panels receive the same `theme` and `layout` fields.
Client code can import `react`, `react-native`, `@tanstack/react-query`, `zod`, `@getpaseo/plugin`, and `@getpaseo/plugin/server`. Install them locally for typechecking; Paseo provides the client runtime instances.
## Contribute a theme
`addTheme` adds a light or dark theme to Settings → Appearance, listed under the built-ins by its `name`. A
theme is data, so it needs no client file:
```ts
import type { PluginContext } from "@getpaseo/plugin";
export default function contribute(plugin: PluginContext) {
plugin.addTheme({
id: "mocha",
name: "Catppuccin Mocha",
appearance: "dark",
colors: {
background: "#1e1e2e",
foreground: "#cdd6f4",
raised: "#313244",
control: "#45475a",
border: "#45475a",
accent: "#cba6f7",
mutedForeground: "#a6adc8",
ring: "#6c7086",
},
});
return () => {};
}
```
Every color is a hex string; anything else fails to load. Paseo expands the palette into the full
token set the built-in dark themes use, so a contributed theme covers panels, menus, diffs, status
colors, and the terminal without listing them.
| Color | Becomes |
| ----------------- | ----------------------------------------------------------------- |
| `background` | App, workspace, and terminal background |
| `foreground` | Primary text, terminal foreground and cursor |
| `raised` | Cards, popovers, and hovered rows |
| `control` | Inputs, secondary fills, and the light-theme sidebar |
| `border` | Borders and the highest raised-surface tint |
| `accent` | Buttons, selection, and focus. Optional; `foreground` if omitted. |
| `mutedForeground` | Secondary text |
| `ring` | Focus rings, scrollbars, and terminal bright black |
`appearance` is `"light"` or `"dark"`. Paseo uses it to select the matching surface, status,
diff, syntax, terminal, and shadow derivation.
Only one contributed theme is active at a time. Selecting one persists the choice; if the plugin is
later disabled or removed, Paseo falls back to the default theme rather than leaving the app
unpainted.
Themes need a host that supports them. A daemon released before `addTheme` compiles the call into
the plugin's backend bundle, where it does not exist, and the plugin fails to start with
`plugin.addTheme is not a function`. Update the host.
## Workspace panels
Register one panel for workspace or agent context: