diff --git a/docs/ai/plugin-ui-consistency-plan.md b/docs/ai/plugin-ui-consistency-plan.md index 2e9a9251a3..73f5382265 100644 --- a/docs/ai/plugin-ui-consistency-plan.md +++ b/docs/ai/plugin-ui-consistency-plan.md @@ -1,5 +1,10 @@ # Plan: Plugin UI Consistency via CSS Library + Reactive Theme +## Status + +- **Part 2 (CSS Component Library)**: Implemented as a UI Kit element reset in `src/app/plugins/util/plugin-ui-kit.css.ts`. Uses plain element selectors injected at the start of `` (inject-first), so plugin CSS always wins by source order. No class-based components — just styled HTML elements. +- **Part 1 (Reactive Theme Updates / THEME_CHANGE hook)**: Not yet implemented. + ## Goal Make iframe plugin UI more consistent with the main app by: diff --git a/docs/plugin-development.md b/docs/plugin-development.md index 3b7bbe04a3..9779645263 100644 --- a/docs/plugin-development.md +++ b/docs/plugin-development.md @@ -85,21 +85,22 @@ The `manifest.json` file is required for all plugins and defines the plugin's me ### Manifest Fields -| Field | Type | Required | Description | -| ----------------- | -------- | -------- | ------------------------------------------------------------------ | -| `id` | string | ✓ | Unique identifier for your plugin (use kebab-case) | -| `name` | string | ✓ | Display name shown to users | -| `version` | string | ✓ | Semantic version (e.g., "1.0.0") | -| `description` | string | ✓ | Brief description of what your plugin does | -| `manifestVersion` | number | ✓ | Currently must be `1` | -| `minSupVersion` | string | ✓ | Minimum Super Productivity version required | -| `author` | string | | Plugin author name | -| `homepage` | string | | Plugin website or repository URL | -| `icon` | string | | Path to icon file (SVG recommended) | -| `iFrame` | boolean | | Whether plugin uses iframe UI (default: false) | -| `sidePanel` | boolean | | Show plugin in side panel (default: false), requires `iFrame:true` | -| `permissions` | string[] | | The permissions the plugin needs (e.g., ["nodeExecution"]) | -| `hooks` | string[] | | App events to listen to | +| Field | Type | Required | Description | +| ----------------- | -------- | -------- | -------------------------------------------------------------------------------------- | +| `id` | string | ✓ | Unique identifier for your plugin (use kebab-case) | +| `name` | string | ✓ | Display name shown to users | +| `version` | string | ✓ | Semantic version (e.g., "1.0.0") | +| `description` | string | ✓ | Brief description of what your plugin does | +| `manifestVersion` | number | ✓ | Currently must be `1` | +| `minSupVersion` | string | ✓ | Minimum Super Productivity version required | +| `author` | string | | Plugin author name | +| `homepage` | string | | Plugin website or repository URL | +| `icon` | string | | Path to icon file (SVG recommended) | +| `iFrame` | boolean | | Whether plugin uses iframe UI (default: false) | +| `sidePanel` | boolean | | Show plugin in side panel (default: false), requires `iFrame:true` | +| `permissions` | string[] | | The permissions the plugin needs (e.g., ["nodeExecution"]) | +| `hooks` | string[] | | App events to listen to | +| `uiKit` | boolean | | Enable UI Kit CSS reset for iframe plugins (default: true). Set to `false` to disable. | ### Complete Manifest Example @@ -172,37 +173,22 @@ Plugins that render custom UI in a sandboxed iframe. My Plugin UI - + @@ -249,6 +235,46 @@ Plugins that render custom UI in a sandboxed iframe. ``` +### Theme Variables & UI Kit + +Iframe plugins automatically receive: + +1. **CSS variables** — All theme variables (colors, spacing, shadows, transitions) are injected as CSS custom properties on `:root`. Use `var(--c-primary)`, `var(--bg)`, `var(--text-color)`, etc. + +2. **UI Kit CSS reset** — By default, basic HTML elements (`button`, `input`, `select`, `textarea`, `table`, `a`, `h1`–`h6`, `p`, `code`, `pre`, `hr`, etc.) are styled to match the app's look. This is injected before your plugin's own styles, so your CSS always wins. + + To disable the UI Kit, add `"uiKit": false` to your manifest. + +**Button variants:** + +- Default ` void; variant?: 'primary' | 'secondary' | 'back'; - size?: 'small' | 'medium' | 'large'; disabled?: boolean; title?: string; class?: string; @@ -12,18 +11,15 @@ interface ButtonProps { export const Button: Component = (props) => { const getButtonClass = () => { - const baseClass = 'button'; - const variantClass = props.variant - ? `${baseClass}-${props.variant}` - : 'action-button primary'; - const sizeClass = props.size ? `${baseClass}-${props.size}` : ''; const customClass = props.class || ''; - - if (props.variant === 'back') { - return 'back-button'; + switch (props.variant) { + case 'back': + case 'secondary': + return `btn-outline ${customClass}`.trim(); + case 'primary': + default: + return `btn-primary ${customClass}`.trim(); } - - return `${variantClass} ${sizeClass} ${customClass}`.trim(); }; return ( diff --git a/packages/plugin-dev/api-test-plugin/index.html b/packages/plugin-dev/api-test-plugin/index.html index 21cd820dae..355ff3c3f5 100644 --- a/packages/plugin-dev/api-test-plugin/index.html +++ b/packages/plugin-dev/api-test-plugin/index.html @@ -8,214 +8,230 @@ /> API Test Plugin Dashboard

API Test Plugin

-
-

Available API Methods

-
-
getTasks()
-
getArchivedTasks()
-
getCurrentContextTasks()
-
addTask(data)
-
updateTask(id, data)
-
getAllProjects()
-
addProject(data)
-
updateProject(id, data)
-
getAllTags()
-
addTag(data)
-
updateTag(id, data)
-
showSnack(config)
-
notify(config)
-
openDialog(config)
-
persistDataSynced(data)
-
loadSyncedData()
-
registerHook(hook, handler)
-
registerHeaderButton(config)
-
registerMenuEntry(config)
-
registerShortcut(config)
-
setCounter(key, value)
-
getCounter(key)
-
incrementCounter(key, amount)
-
decrementCounter(key, amount)
-
deleteCounter(key)
-
getAllCounters()
-
+

Available API Methods

+

+ getTasks() + getArchivedTasks() + getCurrentContextTasks() + addTask(data) + updateTask(id, data) + getAllProjects() + addProject(data) + updateProject(id, data) + getAllTags() + addTag(data) + updateTag(id, data) + showSnack(config) + notify(config) + openDialog(config) + persistDataSynced(data) + loadSyncedData() + registerHook(hook, handler) + registerHeaderButton(config) + registerMenuEntry(config) + registerShortcut(config) + setCounter(key, value) + getCounter(key) + incrementCounter(key, amount) + decrementCounter(key, amount) + deleteCounter(key) + getAllCounters() +

+
+ + + +
+ +

Data Persistence Tests

+
+ +
- - -
-

Data Persistence Tests

-
- - -
+

Task Operations

+
+ + + + +
-
-

Task Operations

-
- - - - - -
+

Project & Tag Operations

+
+ + + +
-
-

Project & Tag Operations

-
- - - - -
+

UI Operations

+
+ + + + +
-
-

UI Operations

-
- - - - - -
+

Advanced Dialog Examples

+
+ +
-
-

Advanced Dialog Examples

-
- - -
-
- -
-
Console output will appear here...
+ >Console output will appear here... + +
+ +

UI Kit Component Showcase

+ +

Headings

+

Heading 1

+

Heading 2

+

Heading 3

+

Heading 4

+
Heading 5
+
Heading 6
+ +

Text

+

+ This is a paragraph with bold, italic, and + link styles. +

+

Inline code is styled automatically.

+ +

Buttons

+
+ + + + + +
+

Inputs

+ + + + + +

Code Block

+
const tasks = await PluginAPI.getTasks();
+console.log(tasks.length);
+ +

Table

+ + + + + + + + + + + + + + + + + + + + + +
MethodDescription
getTasks()Get all active tasks
addTask(data)Create a new task
showSnack(cfg)Show a notification
+ +

Lists

+
    +
  • Unordered list item 1
  • +
  • Unordered list item 2
  • +
  • Unordered list item 3
  • +
+
    +
  1. Ordered list item 1
  2. +
  3. Ordered list item 2
  4. +
  5. Ordered list item 3
  6. +
+ +

Cards

+
+

Default Card

+

A static card with background, shadow, and border.

+
+
+
+

Clickable Card

+

Hover to see the lift effect and primary border highlight.

+
+ +

Horizontal Rule

+
+