mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
* refactor(web): split api_v3/plugins.py by area
web_interface/blueprints/api_v3/plugins.py (3,285 lines) becomes:
- plugins.py: installed list, enable/disable, plugin actions
- plugin_store.py: install, update, uninstall, store, saved repositories
- plugin_config.py: config get/save, schema, reset
- plugin_assets.py: asset uploads and plugin static files
- plugin_health.py: health, metrics, limits
- plugin_operations.py: operation history, state reconciliation
- plugin_calendar.py: calendar credentials and auth
Pure move: all 44 functions and 38 route decorators are byte-identical
(checked with ast), URLs and endpoint names are unchanged (url-map test).
Each module imports only what it uses. Tests and config.py that reached
into plugins.py for moved names now import from the new module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): keep exception text out of calendar responses; annotate moved code
The split made scanners report existing findings in the moved code as new:
- CodeQL: the calendar auth and calendar-list routes returned exception
text (redacted, but still derived from the exception). Both now log the
exception and return a fixed message pointing at the log.
- MD5 in the asset upload only makes a filename unique: usedforsecurity=False.
- pickle reads/writes the calendar plugin's own OAuth token (as before):
annotated. Token-status labels and a log line naming the secrets path are
false positives: annotated with the repo's nosec/nosemgrep convention.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): name uploaded assets with SHA-256 instead of MD5
The hash only makes an uploaded image's filename unique. Codacy flags MD5
even with usedforsecurity=False, and SHA-256 does the job as well; existing
files keep their names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): keep the redacted exception detail in calendar errors
Reverts the calendar part of 5e695b7c. The project's policy
(test_no_api_v3_handler_discards_its_exception) is that an API error
carries the redacted exception detail -- describe_exception runs it
through the credential redactor -- so a failure is diagnosable from the web
UI. Dropping it for CodeQL broke that; CodeQL can't see the redaction, so
its two alerts here are false positives, like the existing ones on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
280 lines
8.0 KiB
Markdown
280 lines
8.0 KiB
Markdown
# Plugin Configuration Tabs
|
|
|
|
## Overview
|
|
|
|
Each installed plugin now gets its own dedicated configuration tab in the web interface. This provides a clean, organized way to configure plugins without cluttering the **Plugin Manager** tab.
|
|
|
|
## Features
|
|
|
|
- **Automatic Tab Generation**: When a plugin is installed, a new tab is automatically created in the web UI
|
|
- **JSON Schema-Based Forms**: Configuration forms are automatically generated based on each plugin's `config_schema.json`
|
|
- **Type-Safe Inputs**: Form inputs are created based on the JSON Schema type (boolean, number, string, array, enum)
|
|
- **Default Values**: All fields show current values or fallback to schema defaults
|
|
- **Real-Time Validation**: Input constraints from JSON Schema are enforced (min, max, maxLength, etc.)
|
|
|
|
## User Experience
|
|
|
|
### Accessing Plugin Configuration
|
|
|
|
1. Navigate to the **Plugin Manager** tab to see all installed plugins
|
|
2. Click the **Configure** button on any plugin card
|
|
3. You'll be automatically taken to that plugin's configuration tab
|
|
4. Alternatively, click directly on the plugin's tab button in the second nav row
|
|
|
|
### Configuring a Plugin
|
|
|
|
1. Open the plugin's configuration tab
|
|
2. Modify settings using the generated form
|
|
3. Click **Save Configuration**. The settings apply to the running display
|
|
without a restart: the display service reloads `config.json` when it
|
|
changes and calls the plugin's `on_config_change()`
|
|
|
|
The tab also has **Refresh** (reload the form), **Update** (update the
|
|
plugin) and **Uninstall** buttons.
|
|
|
|
### Plugin Manager vs Per-Plugin Configuration
|
|
|
|
- **Plugin Manager tab** (second nav row): used for browsing the
|
|
Plugin Store, installing plugins, toggling installed plugins on/off,
|
|
and updating/uninstalling them
|
|
- **Per-plugin tabs** (one per installed plugin, also in the second
|
|
nav row): used for configuring that specific plugin's behavior and
|
|
settings via a form auto-generated from its `config_schema.json`
|
|
|
|
## For Plugin Developers
|
|
|
|
### Requirements
|
|
|
|
Every installed plugin gets a tab. To get a generated form in it, include a
|
|
`config_schema.json` file in the plugin's directory. The name is fixed: the
|
|
web interface finds the schema by that file name (`SchemaManager` in
|
|
`src/plugin_system/schema_manager.py`), and no manifest field points to it.
|
|
|
|
**Note:** You can optionally specify a Font Awesome `icon` class for your
|
|
plugin tab in `manifest.json`. See [Plugin Custom Icons Guide](PLUGIN_CUSTOM_ICONS.md) for details.
|
|
|
|
### Supported JSON Schema Types
|
|
|
|
The form generator supports the following JSON Schema types:
|
|
|
|
#### Boolean
|
|
|
|
```json
|
|
{
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Enable or disable this feature"
|
|
}
|
|
```
|
|
|
|
Renders as: Toggle switch
|
|
|
|
#### Number / Integer
|
|
|
|
```json
|
|
{
|
|
"type": "integer",
|
|
"default": 60,
|
|
"minimum": 1,
|
|
"maximum": 300,
|
|
"description": "Update interval in seconds"
|
|
}
|
|
```
|
|
|
|
Renders as: Number input with min/max constraints
|
|
|
|
#### String
|
|
|
|
```json
|
|
{
|
|
"type": "string",
|
|
"default": "Hello, World!",
|
|
"minLength": 1,
|
|
"maxLength": 50,
|
|
"description": "The message to display"
|
|
}
|
|
```
|
|
|
|
Renders as: Text input with length constraints
|
|
|
|
#### Array
|
|
|
|
```json
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "integer",
|
|
"minimum": 0,
|
|
"maximum": 255
|
|
},
|
|
"minItems": 3,
|
|
"maxItems": 3,
|
|
"default": [255, 255, 255],
|
|
"description": "RGB color [R, G, B]"
|
|
}
|
|
```
|
|
|
|
Renders as: Text input (comma-separated values)
|
|
Example input: `255, 128, 0`
|
|
|
|
#### Enum (Select)
|
|
|
|
```json
|
|
{
|
|
"type": "string",
|
|
"enum": ["small", "medium", "large"],
|
|
"default": "medium",
|
|
"description": "Display size"
|
|
}
|
|
```
|
|
|
|
Renders as: Dropdown select
|
|
|
|
### Example config_schema.json
|
|
|
|
```json
|
|
{
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"type": "object",
|
|
"title": "My Plugin Configuration",
|
|
"description": "Configure my awesome plugin",
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Enable or disable this plugin"
|
|
},
|
|
"message": {
|
|
"type": "string",
|
|
"default": "Hello!",
|
|
"minLength": 1,
|
|
"maxLength": 50,
|
|
"description": "The message to display"
|
|
},
|
|
"update_interval": {
|
|
"type": "integer",
|
|
"default": 60,
|
|
"minimum": 1,
|
|
"maximum": 3600,
|
|
"description": "Update interval in seconds"
|
|
},
|
|
"color": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "integer",
|
|
"minimum": 0,
|
|
"maximum": 255
|
|
},
|
|
"minItems": 3,
|
|
"maxItems": 3,
|
|
"default": [255, 255, 255],
|
|
"description": "RGB color [R, G, B]"
|
|
},
|
|
"mode": {
|
|
"type": "string",
|
|
"enum": ["scroll", "static", "fade"],
|
|
"default": "scroll",
|
|
"description": "Display mode"
|
|
}
|
|
},
|
|
"required": ["enabled"],
|
|
"additionalProperties": false
|
|
}
|
|
```
|
|
|
|
### Best Practices
|
|
|
|
1. **Use Descriptive Labels**: The `description` field is shown as help text under each input
|
|
2. **Set Sensible Defaults**: Always provide default values that work out of the box
|
|
3. **Use Constraints**: Leverage min/max, minLength/maxLength to guide users
|
|
4. **Mark Required Fields**: Use the `required` array in your schema
|
|
5. **Organize Properties**: List properties in order of importance
|
|
|
|
### Form Generation Process
|
|
|
|
Forms are rendered on the server, not generated in the browser:
|
|
|
|
1. The web UI loads installed plugins via `/api/v3/plugins/installed` and adds
|
|
a tab button for each one
|
|
2. Opening a tab loads `/v3/partials/plugin-config/<plugin_id>`
|
|
(`web_interface/blueprints/pages_v3.py`), which loads the plugin's schema
|
|
through `SchemaManager` and its current values from `config.json`
|
|
3. `web_interface/templates/v3/partials/plugin_config.html` renders the form
|
|
from the schema (widgets named by `x-widget` are rendered by the scripts in
|
|
`web_interface/static/v3/js/widgets/`)
|
|
4. **Save Configuration** posts the form to `/api/v3/plugins/config`
|
|
(`web_interface/blueprints/api_v3/plugin_config.py`), which validates it against
|
|
the schema, writes `config.json` (secret fields go to
|
|
`config_secrets.json`) and shows a notification
|
|
|
|
## Troubleshooting
|
|
|
|
### Plugin Tab Not Appearing
|
|
|
|
- Check that the plugin is installed and appears in the **Plugin Manager** tab
|
|
- Check browser console for errors
|
|
- Reload the page
|
|
|
|
### Form Not Generating Correctly
|
|
|
|
- Ensure `config_schema.json` exists in the plugin directory
|
|
- Validate your `config_schema.json` against JSON Schema Draft 07
|
|
- Check that all properties have a `type` field
|
|
- Ensure `default` values match the specified type
|
|
- Look for JavaScript errors in browser console
|
|
|
|
### Configuration Not Saving
|
|
|
|
- Ensure the plugin is properly installed
|
|
- Check that config keys match schema properties
|
|
- Verify backend API is accessible
|
|
- Check browser network tab for API errors
|
|
|
|
## Migration Guide
|
|
|
|
### For Existing Plugins
|
|
|
|
If your plugin already has a `config_schema.json`:
|
|
|
|
1. No changes needed! The tab will be automatically generated.
|
|
2. Test the generated form to ensure all fields render correctly.
|
|
3. Consider adding more descriptive `description` fields.
|
|
|
|
If your plugin doesn't have a config schema:
|
|
|
|
1. Create `config_schema.json` based on your current config structure
|
|
2. Add descriptions for each property
|
|
3. Set appropriate defaults
|
|
4. Add validation constraints (min, max, etc.)
|
|
|
|
### Backward Compatibility
|
|
|
|
- Plugins without `config_schema.json` still work normally
|
|
- Their tab shows plain text, number and checkbox inputs for the keys already
|
|
in their `config.json` section, or "No configuration options available for
|
|
this plugin." when there are none
|
|
- Users can still edit config via the Raw JSON editor
|
|
|
|
## Beyond the Basic Types
|
|
|
|
Nested objects (rendered as collapsible sections), `x-widget` widgets such as
|
|
`color-picker` and `file-upload`, and more are supported; see
|
|
[PLUGIN_CONFIGURATION_GUIDE.md](PLUGIN_CONFIGURATION_GUIDE.md) and
|
|
`web_interface/static/v3/js/widgets/README.md`.
|
|
|
|
## Example Plugins
|
|
|
|
See these plugins for examples of config schemas:
|
|
|
|
- `hello-world`: Simple plugin with basic types
|
|
- `clock-simple`: Plugin with enum and number types
|
|
|
|
## Support
|
|
|
|
For questions or issues:
|
|
- Check the main LEDMatrix wiki
|
|
- Review plugin documentation
|
|
- Open an issue on GitHub
|
|
- Join the community Discord
|
|
|