Plugin Structure
This document provides a detailed description of each part of a plugin.
manifest.json
This file describes the basic information about the plugin and what it contains. You need to specify the keys and language resources your plugin provides in manifest.json.
Below are some important sections:
{
"name": "{{name}}", // Plugin name
"uuid": "{{uuid}}", // Plugin UUID, consisting of three parts in reverse domain format, e.g., com.eniac.example
"version": "{{version}}", // Plugin version number, must be in x.x.x format
"author": "{{author}}", // Author name
"entry": "backend/plugin.cjs", // Plugin backend entry, the compiled Node.js script
"description": "{{description}}", // Plugin description
"repo": "{{repo}}", // Plugin's repository. It must be hosted on a Git repository to enable automatic downloads and updates.
"configPage": "", // Points to a .vue file in the ui folder (e.g., ui/configPage.vue). This can be left blank. If specified, your plugin's settings page will appear in the global settings.
"shortcuts": ["CommandOrControl+F1"], // Register one or more shortcuts, see available shortcuts at https://www.electronjs.org/docs/latest/api/accelerator
"keyLibrary": { // Describes the keys included in the plugin
"title": "$PluginName", // Title of the key library. If the title starts with $, it will look up the corresponding translation in the local section.
"style": {
"icon": "mdi mdi-puzzle" // Icon of the key library. You can use an MDI icon (https://pictogrammers.com/library/mdi/) or a PNG image in base64.
"flags": [], // Controls detailed key behaviors with flags
},
"children": [ // Subdirectories where you can add multiple keys or nested subdirectories. See the keyLibrary section for details.
]
},
"local": { // Multi-language resources. Text starting with $ in the keyLibrary section or used with $t in .vue files is looked up here.
"en": {
"PluginName": "{{name}}"
},
"ja": {
}
}
}
keyLibrary
In keyLibrary, you can add multiple keys or subpages.
The following types are supported:
Default Key
Creates a key that performs an action when pressed or displays a static image.
{
"title": "$Counter.Title", // Title, supports multi-language text starting with $
"tip": "$Counter.Tip", // Brief description, supports multi-language text starting with $
"cid": "com.eniac.example.counter", // classid, must follow plugin-uuid.<key-id> format and be unique
"config": {
"keyType": "default", // Specifies a default key
"clickable": true, // Whether the key is clickable. If clicked, it sends the key data to the plugin backend. If not clickable, it's typically used for static displays (e.g., weather info).
"platform": [ // Supported operating systems
"windows",
"mac"
]
},
"style": { // Default style, see the style section
"icon": "mdi mdi-gesture-tap-button",
"width": 240
},
"data": { // Custom key data. The modelValue in the .vue file corresponds to this object.
"rangeMin": "0",
"rangeMax": "100"
}
},
Multi-State Key
Creates a key with multiple states that can be cycled through. For example, on/off, mode A/B/C, etc.
{
"title": "$CycleButton.Title",
"tip": "$CycleButton.Tip",
"cid": "com.eniac.example.cyclebutton",
"config": {
"keyType": "multiState" // Specifies a multi-state key
},
"style": {
"icon": "mdi mdi-record-circle-outline",
"width": 240,
"multiStyle": [ // Specifies the default style for each state in multiStyle; each entry supports all settings from the style section
{
"icon": "mdi mdi-numeric-0-box",
"bgColor": "#D6582A"
},
{
"icon": "mdi mdi-numeric-1-box",
"bgColor": "#CD3B42"
},
{
"icon": "mdi mdi-numeric-2-box",
"bgColor": "#C33DA3"
}
]
},
"data": {
}
},
Slider
Creates a slider control, suitable for functions that require drag control with absolute values, such as volume control.
{
"title": "$Slider.Title",
"tip": "$Slider.Tip",
"cid": "com.eniac.example.slider",
"config": {
"keyType": "slider" // Specifies a slider
},
"style": {
"icon": "mdi mdi-tune-variant",
"width": 360,
"slider": { // Specifies the slider style
"color": "#ffffff", // Slider theme color
"width": 260, // Slider width, cannot exceed the key width
"format": "%0.1f %%", // Value display format, supports C-style formatting
"min": 0, // Minimum value
"max": 100, // Maximum value
"decimals": 1 // Number of decimal places
}
},
"data": {
}
},
Wheel
Creates a wheel control, suitable for parameter adjustments that don’t have absolute values.
{
"title": "$Wheel.Title",
"tip": "$Wheel.Tip",
"cid": "com.eniac.example.wheel",
"config": {
"keyType": "wheel" // Specifies a wheel
},
"style": {
"icon": "mdi mdi-tire",
"width": 400,
"wheel": {
"step": 5 // Sets the wheel sensitivity; it triggers once every 5 steps
}
},
"data": {
}
},
Direct Draw (new)
Requires FlexDesigner SDK 1.0.7+
Creates an empty page that allows you to directly draw custom content at a relatively fast speed (depending on image content, approximately 15~45fps). You can use this feature to create complex UIs and even animations. You can also receive touch information reported by the device.
{
"title": "$DirectDraw.Title",
"tip": "$DirectDraw.Tip",
"cid": "com.eniac.example.directdraw",
"config": {
"keyType": "directDraw", // Specifies DirectDraw
"platform": [
"windows",
"mac"
]
},
"style": {
"icon": "mdi mdi-gradient-horizontal",
"width": 240
},
"data": {
}
}
Direct Draw API
The Direct Draw API is simple and straightforward, with only one directDraw method:
plugin.directDraw(serialNumber, key, backgroundData, diffUpdate, offsetX)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.backgroundData: Base64 encoded image data, such as canvas.toDataURL(‘image/png’)diffUpdate: Whether to use partial updates, defaults to false. If true, it will compare differences between consecutive frames and only update changed parts. This feature can significantly improve frame rate for small area updates, but may occasionally cause screen tearingoffsetX: Horizontal coordinate of the image, range 0-2170. You can specify this parameter to manually refresh specific areas. The image height is fixed at 60px, and the width is specified by the encoded image base64.
Touch Event API
When on a Direct Draw page, the device will report touch information:
plugin.on('device.touch', (payload) => {})
payload: Touch data
{
serialNumber: '',
x: 0,
y: 0,
state: 'up' | 'pressing' | 'down' | 'end'
}
Dynamic Key (new)
Requires FlexDesigner SDK 1.0.6+
Creates a dynamic key that serves as a container. It has no functionality by itself, but you can use APIs to add/remove child keys. This is suitable for projects that require dynamic creation. Currently, a dynamic key can contain up to 16 child keys.
{
"title": "$DynamicKey.Title",
"tip": "$DynamicKey.Tip",
"cid": "com.eniac.example.dynamickey",
"config": {
"keyType": "dynamic", // Specifies a dynamic key
"platform": [
"windows",
"mac"
]
},
"style": { // For Dynamic Key, users can only adjust the width. Other style properties only affect the display in FlexDesigner and will not be shown on the flexbar.
"icon": "mdi mdi-contain",
"width": 240
},
"data": {
"subkeyNum": 0 // This value will be automatically updated when you add/remove keys. You don't need to set this item.
}
}
Dynamic Key API
Here are some code snippets demonstrating how to interact with Dynamic Keys. For more detailed examples, please refer to the Example Plugin.
index: Refers to the index of the child key. This index is specific to the Dynamic Key’s internal independent indexing, starting from 0. If there are multiple Dynamic Keys, their indices are independent of each other.
userData: Refers to the user-defined data bound to the child key. When you press a child key, this data will be sent to the plugin. You can use this data to distinguish and define the functionality of child keys.
Set Key Width
plugin.dynamickey.setWidth(serialNumber, key, width)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.width: New width value (in pixels)
Add Child Key
plugin.dynamickey.add(serialNumber, key, index, type, content, width, userData)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.index: Insert positiontype: Content type (‘base64’,’draw’)content: Key drawing content (base64 image or an Object describing key structure and Style)width: Key widthuserData: User data object
The drawing part is similar to the plugin.draw method
Remove Child Key
plugin.dynamickey.remove(serialNumber, key, index)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.index: Index of the key to remove
Move Child Key Position
plugin.dynamickey.move(serialNumber, key, fromIndex, toIndex)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.fromIndex: Source positiontoIndex: Target position
Redraw Child Key
plugin.dynamickey.draw(serialNumber, key, index, type, content, width)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.index: Key indextype: Content type (‘base64’,’draw’)content: Key drawing content (base64 image or an Object describing key structure and Style)width: New key width
The drawing part is similar to the plugin.draw method
Update Child Key User Data
plugin.dynamickey.update(serialNumber, key, index, userData)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.index: Key indexuserData: New user data object
Refresh Key Display
plugin.dynamickey.refresh(serialNumber, key)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.
After modifying the key width, it’s recommended to wait 50ms for the update to complete before calling this method to refresh the display
Clear All Child Keys
plugin.dynamickey.clear(serialNumber, key)
serialNumber: Device serial numberkey: Key object received from the eventplugin.dataorplugin.alive.
Subpage
Adds a subpage/category. You can use this feature to categorize your plugin’s keys.
{
"title": "$Submenu.Title",
"cid": "com.eniac.navigation.page", // Must be com.eniac.navigation.page, cannot be changed
"style": {
"icon": "mdi mdi-folder-outline",
"width": 240
},
"data": {
"path": "",
"win": []
},
"children": [ // Keys contained in this subpage
]
}
style
Defines the default style of a key. The following fields are supported:
{
"icon": "mdi mdi-cog", // Icon for the key. Can be an MDI icon (https://pictogrammers.com/library/mdi/) or a PNG in base64
"emoji": "☺️", // An emoji for the key
"width": 240, // Width of the key
"bgColor": "#424242", // Background color
"fgColor": "#ffffff", // Foreground color
"borderStyle": "none", // Border style: solid, dotted, double, 3d
"borderWidth": 1, // Border width
"borderColor": "#ffffff", // Border color
"font": "", // Font for the key title
"fontSize": 24, // Font size for the key title
"iconSize": 42, // Icon size
"iconPos": { // Icon position in percentages
"X": 50,
"Y": 49
},
"titlePos": { // Title position in percentages
"X": 50,
"Y": 50
},
"titleRotate": 0, // Rotation angle for the title
"iconRotate": 0, // Rotation angle for the icon
"foregroundOutline": true, // Whether to add a shadow to the icon and title
"showIcon": true, // Whether to display the icon
"showEmoji": false, // Whether to display an emoji
"showTitle": false, // Whether to display the title
"showImage": false, // Whether to display an image
"image": "<base64>" // A base64-encoded PNG background image, valid only if showImage is true
}
flags
Controls more detailed interaction behaviors, such as disabling background editing. Available flags:
disable-bg: Disables the background editing window
disable-fg: Disables the foreground editing window
disable-func: Disables the function editing window
disable-common: Disables the common function editing window
disable-bg-styles: Disables background style editing, but retains width adjustment
disable-icon-sel: Prevents icon selection
disable-layout-sel: Prevents layout selection
local
Includes multilingual resources. Text starting with $ in the keyLibrary section or used with $t in .vue files is looked up here.
"local": {
"en": { // Language code, see below
"PluginName": "{{name}}" // Use it via $t("PluginName") in .vue file, or $PluginName in keyLibrary section.
}
}
Supported language code:
Code |
Language |
|---|---|
en |
English |
de |
German |
fr |
French |
ja |
Japanese |
zh-CN |
Chinese (Simplified) |
zh-HK |
Chinese (Traditional) |
ko |
Korean |
backend
Holds the compiled script. The main process will launch this script via manifest.entry.
resources
Stores resource files (e.g., images). You can access them as follows:
const { resourcesPath } = require("@eniac/flexdesigner")
ui
Holds frontend .vue files. The .vue file name can match the one specified in manifest.configPage for the settings interface or match each key’s cid in keyLibrary. For example, the key com.eniac.example.counter should have a corresponding ui/com.eniac.example.counter.vue file.