How to Extend PicList's Functionality: A Developer's Guide to Building PicGo Plugins

Developers can extend PicList by creating standard PicGo GUI plugins using the picgo-plugin- naming convention, which the application automatically discovers, loads, and integrates into the Settings interface without requiring modifications to the core codebase.

PicList, maintained by kuingsmile/piclist, is an image upload and management tool built on top of the PicGo framework. Because it inherits PicGo's robust plugin architecture, developers can extend PicList's functionality by creating custom uploaders, transformers, and GUI menus that integrate seamlessly with the existing Electron-based interface.

Understanding the Plugin Architecture

PicList's extensibility is driven entirely by the PicGo plugin system. The application acts as a consumer of PicGo GUI plugins, handling discovery, state management, and UI rendering through a dedicated IPC layer.

Plugin Discovery and Loading

On startup, PicList invokes picgo.pluginLoader.getFullList() to scan the application's node_modules directory for packages matching the picgo-plugin-* pattern. In src/main/events/rpc/routes/plugin/utils.ts (lines 45-70), the loader reads each plugin's package.json to verify it is a GUI plugin by checking for the picgo-gui-plugin keyword in the package metadata.

Metadata Extraction and State Management

For each discovered plugin, PicList constructs an IPicGoPlugin object containing the name, version, author, description, logo, GUI flag, and configuration schemas for the plugin, uploader, and transformer. This process occurs in src/main/events/rpc/routes/plugin/utils.ts (lines 71-99). The enabled state is synchronized from the global configuration key picgoPlugins.<fullName>, defined in src/main/utils/configPaths.ts (line 94), allowing PicList to persist user preferences across sessions.

RPC Communication and GUI Rendering

The pluginRouter in src/main/events/rpc/routes/plugin/index.ts handles UI actions such as install, uninstall, update, and import-local operations, forwarding results via Electron IPC using event.sender.send. When a plugin exports a guiMenu(picgo) function, PicList maps each menu item to a label object in src/main/events/rpc/routes/plugin/utils.ts (lines 58-63), displaying these items under Settings → Plugins. Configuration schemas are processed through handleConfigWithFunction in src/main/utils/common.ts (lines 33-42) to evaluate dynamic functions before rendering forms in the renderer process.

Creating a Custom PicList Plugin

A PicList plugin is a Node.js module that conforms to the PicGo GUI plugin contract. The package must be published to npm or installed locally, following specific structural requirements.

Package Structure and Naming

Your npm package name must start with picgo-plugin-. The package must export a default object conforming to the IPlugin interface and include picgo-gui-plugin in the keywords array of package.json to ensure PicList recognizes it during the discovery phase.

Implementing an Uploader

The core functionality resides in the uploader method, which returns an object with a name identifier and an async handle function that processes image data.

// index.ts – the only file that will be published to npm
import type { IPlugin, PicGo } from 'picgo'

// The plugin object follows PicGo's GUI plugin contract
const plugin: IPlugin = {
  // uploader implementation – called by PicGo when an image is uploaded
  uploader (ctx: PicGo) {
    return {
      name: 'hello',
      // handle must return an array of { url } objects
      async handle (ctx) {
        // For demonstration we just build a static URL
        const fileName = ctx.output[0].fileName
        const url = `https://example.com/${fileName}`
        ctx.log.info('hello-uploader →', url)
        return [{ url }]
      }
    }
  },

  // optional GUI menu – appears under Settings → Plugins → Hello Uploader
  guiMenu (picgo) {
    return [
      {
        label: 'Upload current clipboard image with Hello',
        async handle () {
          const img = await picgo.helper.getClipboardFile()
          const result = await picgo.upload([img])
          picgo.emit('notification', `Uploaded: ${result[0].url}`)
        }
      }
    ]
  },

  // optional plugin-level config (shown in the Settings UI)
  config (ctx) {
    return [
      {
        name: 'prefix',
        type: 'input',
        default: '',
        message: 'Optional URL prefix',
        required: false
      }
    ]
  }
}

export default plugin

Adding GUI Menus and Configuration

To expose functionality in the PicList interface, export a guiMenu function that returns an array of menu items. Each item requires a label string and a handle function. For configurable options, export a config function returning an array of configuration objects that define input types, defaults, and validation rules. The handleConfigWithFunction utility in src/main/utils/common.ts processes these schemas to support dynamic default values.

Persisting Plugin State

When users toggle a plugin on or off, PicList updates the configuration via picgo.setConfig at the key picgoPlugins.<fullName> and notifies the renderer with the PICGO_TOGGLE_PLUGIN event, as implemented in src/main/events/remotes/menu.ts (lines 284-303).

Key Source Files in the Plugin Lifecycle

The following files form the complete pipeline for plugin discovery, configuration, and execution:

Summary

  • PicList extends PicGo's plugin architecture, accepting any npm package prefixed with picgo-plugin- and tagged with picgo-gui-plugin.
  • Plugins are discovered automatically via picgo.pluginLoader.getFullList() in src/main/events/rpc/routes/plugin/utils.ts (lines 45-70).
  • GUI integration requires exporting guiMenu and config functions, which the RPC layer processes to generate the Settings interface.
  • Configuration persistence uses the picgoPlugins.<fullName> key defined in src/main/utils/configPaths.ts (line 94).
  • The renderer communicates with the main process through the pluginRouter in src/main/events/rpc/routes/plugin/index.ts to manage the plugin lifecycle.

Frequently Asked Questions

What naming convention must I follow to create a PicList plugin?

Your npm package name must start with picgo-plugin- and include the picgo-gui-plugin keyword in package.json for PicList to recognize it as a GUI plugin during the discovery phase in src/main/events/rpc/routes/plugin/utils.ts. Without this keyword, the loader will not identify your package as a GUI plugin.

How does PicList handle plugin configuration UI?

PicList retrieves the config array from your plugin and processes it through handleConfigWithFunction in src/main/utils/common.ts (lines 33-42) to evaluate any dynamic functions, then renders the resulting schema as dynamic forms in the Settings interface under your plugin's name.

Can I add custom keyboard shortcuts for my PicList plugin?

Yes, you can register custom shortcuts by invoking the PicList CLI's shortkey service during your plugin's post-install phase, or by handling keyboard events within your guiMenu implementation that communicates via the RPC layer in src/main/events/rpc/routes/plugin/index.ts. The main process will bind these shortcuts to your plugin's handlers.

Where does PicList store plugin enable and disable states?

The enabled state is stored in the global configuration under the key picgoPlugins.<fullName>, defined in src/main/utils/configPaths.ts (line 94), and updated via picgo.setConfig when users toggle plugins in the interface, with changes synchronized through the PICGO_TOGGLE_PLUGIN event.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →