Skip to content

Seizo Plugin Development

Do you want to add more functionality to your website, or do you have an idea for a new feature? This guide explains how to build a plugin for the Seizo Desktop app. First, let’s cover how Seizo Desktop detects installed plugins. The file at /src/content/sz-plugin.json is what Seizo Desktop reads to detect plugins. Inside it is an array of objects that define the plugins.

/src/content/sz-plugin.json
[
{
"pluginUid": "sei-blog",
"pluginStatus": "active",
"pluginName": "Sei Blog Plugin",
"pluginVersion": "1.0.0",
"pluginSource": "local",
"pluginRemotePath": "C:\\Users\\name\\Documents\\SeizoZenshin\\plugins\\sei-blog",
"pluginEditor": false,
"pluginAvailableComp": false,
"pluginImg": false
},
{
"pluginUid": "sei-catalog",
"pluginStatus": "installed",
"pluginName": "Sei Catalog Plugin",
"pluginVersion": "1.0.0",
"pluginSource": "github",
"pluginRemotePath": "C:\\Users\\name\\Documents\\SeizoZenshin\\plugins\\sei-catalog",
"pluginEditor": false,
"pluginAvailableComp": false,
"pluginImg": false
},
{
"pluginUid": "sei-shop",
"pluginStatus": "deactive",
"pluginName": "Sei Shop Plugin",
"pluginVersion": "1.0.0",
"pluginSource": "private",
"pluginRemotePath": "C:\\Users\\name\\Documents\\SeizoZenshin\\plugins\\sei-shop",
"pluginEditor": false,
"pluginAvailableComp": false,
"pluginImg": false
}
]

The plugin itself lives at the path src/features/plugins/{{pluginUid}}/**. It contains all of the plugin files, including plugin.json, pluginConfig.json, a src folder (containing all uncompiled source files), and a dist folder (containing all compiled source files).

Plugin List and Individual Plugin Explained

Section titled “Plugin List and Individual Plugin Explained”

The plugin list is an array of objects, each representing one plugin. Each object has the following properties:

  • pluginUid: A unique identifier for the plugin.
  • pluginStatus: The status of the plugin (active, deactive, or installed). When the status is installed, the plugin has been downloaded but not yet activated. When the status is active, the plugin is activated and ready to use. When the status is deactive, the plugin is not activated and not ready to use.
  • pluginName: The name of the plugin.
  • pluginVersion: The version of the plugin.
  • pluginSource: The source of the plugin (local, github, or private).
  • pluginRemotePath: The remote path of the plugin, if applicable.
  • pluginEditor: (Type boolean) Whether the plugin appears in the editor tab.
  • pluginImg: (Type string) Whether the plugin appears in the image modal.
  • pluginAvailableComp: (Type boolean) Whether the plugin appears in the available component tab.

The same properties also appear for each individual plugin in the /src/features/plugins/{{pluginUid}}/plugin.json file. Instead of an array of objects, that file contains a single object describing the plugin’s status.

Next, let’s look at the individual plugin configuration properties. They are defined in /src/features/plugins/{{pluginUid}}/pluginConfig.json.

/src/features/plugins/{{pluginUid}}/pluginConfig.json
{
"pluginUid": "sei-blog",
"pluginName": "Sei Blog Plugin",
"pluginVersion": "1.0.0",
"pluginRequireVersionAtLeast": "4.0.0",
"pluginNpmPackageDep": null,
"pluginNpmPackageDevDep": null,
"pluginFrameworkDep": null,
"pluginPage": null,
"pluginAddAvailableComp": {
"layouts": [],
"components": [
{
"componentModule": "Latest Blog",
"componentImg": "",
"componentName": "sei-blog/LatestBlogPost",
"data": {},
"props": []
}
]
},
"pluginActivate": {
"title": "Sei Blog Plugin",
"description": "Please click activate to activate the sei blog plugin",
"widget": [],
"schemaName": null,
"initFuncName": null,
"submitFuncName": "activateFunc"
},
"pluginTabs": [
{
"tabName": "Blog Posts",
"title": "List Of Blog Posts",
"description": null,
"widget": [
{
"label": "Published Table",
"widget": "table",
"name": "blogData"
}
],
"schemaName": null,
"initFuncName": "readBlogPostsFunc",
"submitFuncName": null
}
// ... rest of it ...
],
"pluginEditorTabs": [],
"pluginImgTabs": [],
"pluginAvailableCompTabs": []
}
  • pluginUid: A unique identifier for the plugin.
  • pluginName: The name of the plugin.
  • pluginVersion: The version of the plugin.
  • pluginRequireVersionAtLeast: (e.g. "7.0.0") The minimum Astro version required by the plugin.
  • pluginNpmPackageDep: (null or string[]) The npm package dependencies of the plugin.
  • pluginNpmPackageDevDep: (null or string[]) The npm package development dependencies of the plugin.
  • pluginFrameworkDep: (null or string[]) The framework dependencies of the plugin. Supported frameworks: react, vue, svelte, solid, preact, and alpinejs.
  • pluginPage: (null or an array of pageType objects) You can add pages to the plugin here. Each page is a pageType object, as described in Pages. When the plugin is activated, Seizo Desktop automatically adds the pages to the project.
  • pluginAddAvailableComp: (null or a listOfComponentsLayouts value) You can add available components and layouts to the plugin here. When the plugin is activated, Seizo Desktop automatically adds the components and layouts to the project’s listOfComponentsLayouts.json file.
  • pluginActivate: (a tabType object without tabName) This is the screen users see when activating the plugin. It contains a title, description, an array of widgets, a schema name, an init function, and a submit function. This is where you can define Astro Content Collections, set environment variables, deploy serverless functions, and more.
  • pluginTabs: (an array of tabType objects) You can add tabs to the plugin here. Each tab is a tabType object. After activation, users can see and interact with the tabs. Each tab has a tabName and runs its initFuncName function to display all its widgets. This allows you to read directory files, add or edit files, and remove files for Astro Content Collections. Each tab is rendered for the user and runs its own functions. To hide a tab from the user, set tabName to hidden.
  • pluginEditorTabs: (an array of tabType objects) You can add tabs to the plugin here. This is similar to pluginTabs, but is used for the editor tab.
  • pluginImgTabs: (an array of tabType objects) You can add tabs to the plugin here. This is similar to pluginTabs, but is used for the image tab.

Full plugin config json can be found here.

Now that you have read about the configuration, you may have noticed schemaName, initFuncName, and submitFuncName in the tabType object and wondered what they are for. That is where the src folder and its main.ts file come in. Inside main.ts, you can define the zod schema, init function, and submit function for the tab, and then export them.

These functions are called when the tab is initialized or submitted. Make sure to export them so that the initFuncName and submitFuncName values in your pluginConfig.json match the names of the functions defined in main.ts.

As you know, Seizo Desktop is built with the Tauri framework. Therefore, when you need to read or write files, you will use Tauri APIs. However, I recommend using my Blog plugin as a starter for developing your own plugin. It is available here.

In main.ts, make sure to export all your functions, then run npm run build to build your plugin. This builds your functions into the dist folder and validates the data in pluginConfig.json and plugin.json.

Looking at the functions in readData.ts, you can see that each function receives two inputs: projectDir: string and values: any. These are passed from the tab initialization or submission process, depending on whether the function is the initFuncName or submitFuncName function. The function’s return type defines success, what type of widget to display after the function is called, initData, formData, and naviData.

For the functionReturnDataType output type, see line 128.

The values: any input contains the values from the tab initialization. For example, the initialization form of a widget creates fields for the user to fill in, and those inputs are passed here as values. Make sure to safely parse the values first before saving the data to a file or submitting it to the server. Using my blog plugin as an example, to create a new blog post I take the values from the user input and pass them to the plugin function (see line 6). The projectDir input is the user’s current project directory.

For the output values (functionReturnDataType), success must be true. If success is false, an error is thrown for the user. Next, initData is used to display data back to the user. For example, in the blog plugin’s readBlogPostsFunc function (see line 50), when you need to display data with a table widget, pass the initData to the table widget as an object, exactly as shown at line 50.

Next, formData is used when you need to display form data back to the user. Pass the formData to the widget as an object. For example, when editing a blog post in the blog plugin, you get the current post data and pass it to the widget as an object, as shown at line 47.

Next, the naviTab object has two properties: tabNo and carryData. When you specify a number for tabNo, the app navigates to that tab, and carryData is passed to it. For example, when a user clicks on a blog post, you can use naviTab to navigate to the edit tab and pass the post data along, as shown at line 20.

Next, widgets. Right now you can display tables, lists of images, and lists of thumbnails. This is not exhaustive — more widget types will be added in the future. As previously mentioned, widgets use initData to display data back to the user. Make sure to sort the data before returning it, and make sure the widget is specified as a table in pluginConfig.json with a name value that matches the initData key. For example, in the blog plugin’s pluginConfig.json at line 39, the widget is specified as a table with the name blogData. Correspondingly, in the readBlogPostsFunc function, line 50 defines the name as the key, and line 52 includes the widgets that define the table name and tableColInfo. tableColInfo also lets you set up actions such as a view post button and a delete post function.

If there is a pages folder in your plugin directory, its pages will be copied into the user’s Astro pages directory. Instead of defining pluginPage in pluginConfig.json, you can simply add a folder to pages, and Seizo will automatically copy it into the user’s Astro pages directory. The downside is that users cannot customize the pages. That said, it is much easier to insert the blog slug into the page, as shown here.

If there is a components folder in your plugin directory, its components will be copied into the user’s Astro components directory with the pluginUid as a subdirectory, e.g. /src/features/components/{{pluginUid}}/{{all the component contents here}}. Also, make sure to register your component in pluginConfig.json > pluginAddAvailableComp > components.

First, create a dummy project with Seizo Desktop. Then, create a folder at src/features/plugins/{{pluginUid}}/** inside it. Copy the blog plugin files into this folder as a starter, rewrite the functions, and register the plugin in sz-plugin.json.

Submit your plugin here. Make sure to include all the frontmatter fields from the sample README.md, and make sure the icons and image are included in the root plugin directory.

Thanks for reading this guide. I hope you found it useful. If you have any questions, please feel free to ask.