Documentation
¶
Overview ¶
Package plugin provides a system for bundling UI components into cohesive packages.
Plugins allow developers to distribute collections of widgets, themes, and layouts as a single unit with proper lifecycle management and dependency tracking.
Overview ¶
A plugin implements the Plugin interface and can register any combination of:
- Widgets via registry.WidgetRegistry
- Themes via theme.ThemeRegistry
- Layouts via layout.Registry
- Assets (fonts, icons, images) via AssetLoader
Creating a Plugin ¶
Implement the Plugin interface:
type MyPlugin struct{}
func (p *MyPlugin) Name() string { return "my-plugin" }
func (p *MyPlugin) Version() string { return "1.0.0" }
func (p *MyPlugin) Dependencies() []plugin.Dependency {
return nil // No dependencies
}
func (p *MyPlugin) Init(ctx *plugin.PluginContext) error {
// Register widgets
ctx.Widgets.Register("my-button", NewMyButton)
// Register themes
ctx.Themes.Register("my-theme", myTheme)
// Load assets
ctx.Assets.LoadFont("my-font", fontData)
return nil
}
func (p *MyPlugin) Shutdown() error {
return nil // Cleanup if needed
}
Registering Plugins ¶
Register plugins in your package's init() function:
func init() {
plugin.Register(&MyPlugin{}, plugin.PluginInfo{
Name: "my-plugin",
Description: "My custom UI plugin",
Version: "1.0.0",
Author: "My Team",
License: "MIT",
})
}
Using Plugins ¶
In your application, initialize all registered plugins:
func main() {
// Initialize all plugins
if err := plugin.Initialize(); err != nil {
log.Fatal(err)
}
defer plugin.Shutdown()
// List available plugins
for _, name := range plugin.List() {
info, _ := plugin.Info(name)
fmt.Printf("%s v%s - %s\n", name, info.Version, info.Description)
}
// Use components from plugins
widget, _ := registry.CreateWidget("my-button", nil)
theme, _ := theme.Get("my-theme")
}
Dependencies ¶
Plugins can declare dependencies on other plugins:
func (p *MyPlugin) Dependencies() []plugin.Dependency {
return []plugin.Dependency{
{Name: "base-plugin", Version: ">=1.0.0"},
}
}
The PluginManager automatically resolves dependencies and initializes plugins in the correct order. Circular dependencies are detected and reported as errors.
Version Constraints ¶
Version constraints support semantic versioning:
- "1.0.0" - exact version
- ">=1.0.0" - minimum version
- "<=1.0.0" - maximum version
- ">1.0.0" - greater than
- "<1.0.0" - less than
- ">=1.0.0,<2.0.0" - range (AND)
Thread Safety ¶
All operations on the global PluginManager are thread-safe. Plugins are initialized sequentially in dependency order to avoid race conditions during registration.
Index ¶
- Variables
- func ClearGlobalManager() error
- func Count() int
- func Has(name string) bool
- func Initialize() error
- func InitializeWithContext(ctx *PluginContext) error
- func IsInitialized() bool
- func List() []string
- func LoadOrder() []string
- func MustRegister(plugin Plugin, info ...PluginInfo)
- func Register(plugin Plugin, info ...PluginInfo) error
- func Shutdown() error
- type AssetLoader
- type Dependency
- type MemoryAssetLoader
- func (m *MemoryAssetLoader) Clear()
- func (m *MemoryAssetLoader) FontCount() int
- func (m *MemoryAssetLoader) GetFont(name string) ([]byte, bool)
- func (m *MemoryAssetLoader) GetIcon(name string) ([]byte, bool)
- func (m *MemoryAssetLoader) GetImage(name string) ([]byte, bool)
- func (m *MemoryAssetLoader) IconCount() int
- func (m *MemoryAssetLoader) ImageCount() int
- func (m *MemoryAssetLoader) LoadFont(name string, data []byte) error
- func (m *MemoryAssetLoader) LoadIcon(name string, data []byte) error
- func (m *MemoryAssetLoader) LoadImage(name string, data []byte) error
- type Plugin
- type PluginContext
- type PluginInfo
- type PluginManager
- func (m *PluginManager) AllInfo() []PluginInfo
- func (m *PluginManager) Clear() error
- func (m *PluginManager) Count() int
- func (m *PluginManager) Get(name string) (Plugin, bool)
- func (m *PluginManager) Has(name string) bool
- func (m *PluginManager) Info(name string) (PluginInfo, bool)
- func (m *PluginManager) Initialize() error
- func (m *PluginManager) InitializeWithContext(ctx *PluginContext) error
- func (m *PluginManager) IsInitialized() bool
- func (m *PluginManager) List() []string
- func (m *PluginManager) LoadOrder() []string
- func (m *PluginManager) MustRegister(plugin Plugin, info ...PluginInfo)
- func (m *PluginManager) Register(plugin Plugin, info ...PluginInfo) error
- func (m *PluginManager) Shutdown() error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrPluginNotFound is returned when a plugin is not registered. ErrPluginNotFound = errors.New("plugin not found") // ErrPluginExists is returned when attempting to register a duplicate plugin. ErrPluginExists = errors.New("plugin already registered") // ErrNilPlugin is returned when a nil plugin is provided. ErrNilPlugin = errors.New("plugin cannot be nil") // ErrEmptyName is returned when a plugin has an empty name. ErrEmptyName = errors.New("plugin name cannot be empty") // ErrAlreadyInitialized is returned when Initialize is called twice. ErrAlreadyInitialized = errors.New("plugins already initialized") // ErrNotInitialized is returned when operations require initialized plugins. ErrNotInitialized = errors.New("plugins not initialized") // ErrCircularDependency is returned when circular dependencies are detected. ErrCircularDependency = errors.New("circular dependency detected") // ErrDependencyNotFound is returned when a required dependency is not registered. ErrDependencyNotFound = errors.New("required dependency not found") // ErrVersionMismatch is returned when a dependency version constraint is not satisfied. ErrVersionMismatch = errors.New("version constraint not satisfied") )
Common errors returned by plugin operations.
Functions ¶
func ClearGlobalManager ¶
func ClearGlobalManager() error
ClearGlobalManager removes all plugins from the global manager.
WARNING: This is primarily intended for testing. Use with caution in production code.
func Initialize ¶
func Initialize() error
Initialize loads all registered plugins from the global manager.
Call this at application startup, after all plugins have been registered (typically via init() functions):
func main() {
if err := plugin.Initialize(); err != nil {
log.Fatal(err)
}
defer plugin.Shutdown()
// Application code...
}
func InitializeWithContext ¶
func InitializeWithContext(ctx *PluginContext) error
InitializeWithContext loads plugins using a custom context.
func IsInitialized ¶
func IsInitialized() bool
IsInitialized returns true if the global manager has been initialized.
func MustRegister ¶
func MustRegister(plugin Plugin, info ...PluginInfo)
MustRegister is like Register but panics if registration fails.
Use this in init() functions where registration failure indicates a programming error.
func Register ¶
func Register(plugin Plugin, info ...PluginInfo) error
Register adds a plugin to the global manager.
This is typically called from init() functions:
func init() {
plugin.Register(&MyPlugin{}, plugin.PluginInfo{
Name: "my-plugin",
Description: "My custom plugin",
})
}
Types ¶
type AssetLoader ¶
type AssetLoader interface {
// LoadFont registers a font with the given name.
//
// The data parameter should contain the raw font file data
// (typically TTF or OTF format). The name is used to reference
// the font in typography settings.
//
// Returns an error if the font data is invalid or loading fails.
LoadFont(name string, data []byte) error
// LoadIcon registers an icon with the given name.
//
// The data parameter should contain the icon image data
// (typically PNG or SVG format). The name is used to reference
// the icon in widgets.
//
// Returns an error if the icon data is invalid or loading fails.
LoadIcon(name string, data []byte) error
// LoadImage registers an image with the given name.
//
// The data parameter should contain the image data
// (typically PNG, JPEG, or WebP format). The name is used to
// reference the image in widgets and themes.
//
// Returns an error if the image data is invalid or loading fails.
LoadImage(name string, data []byte) error
}
AssetLoader provides methods for loading plugin resources.
Plugins use the AssetLoader to load fonts, icons, and images during initialization. The loaded assets are then available to widgets and themes throughout the application.
Implementations should be thread-safe as plugins may be initialized concurrently in some scenarios.
Example:
func (p *MyPlugin) Init(ctx *PluginContext) error {
// Load a font
if err := ctx.Assets.LoadFont("roboto", robotoData); err != nil {
return fmt.Errorf("failed to load roboto font: %w", err)
}
// Load icons
if err := ctx.Assets.LoadIcon("add", addIconData); err != nil {
return fmt.Errorf("failed to load add icon: %w", err)
}
// Load images
if err := ctx.Assets.LoadImage("logo", logoData); err != nil {
return fmt.Errorf("failed to load logo: %w", err)
}
return nil
}
type Dependency ¶
type Dependency struct {
// Name is the unique identifier of the required plugin.
Name string
// Version is a semantic version constraint.
//
// Supported formats:
// - "1.0.0" - exact version match
// - ">=1.0.0" - minimum version
// - "<=1.0.0" - maximum version
// - ">1.0.0" - greater than
// - "<1.0.0" - less than
// - ">=1.0.0,<2.0.0" - range (AND condition)
// - "" - any version
Version string
}
Dependency declares a dependency on another plugin.
Plugins can depend on other plugins by name and version constraint. The plugin manager resolves dependencies and initializes plugins in the correct order.
Example:
func (p *MyPlugin) Dependencies() []Dependency {
return []Dependency{
{Name: "base-widgets", Version: ">=1.0.0"},
{Name: "icons-pack", Version: ">=2.0.0,<3.0.0"},
}
}
type MemoryAssetLoader ¶
type MemoryAssetLoader struct {
// contains filtered or unexported fields
}
MemoryAssetLoader is a simple in-memory implementation of AssetLoader.
It stores all loaded assets in memory and provides methods to retrieve them. This is useful for testing and simple applications.
MemoryAssetLoader is thread-safe.
func NewMemoryAssetLoader ¶
func NewMemoryAssetLoader() *MemoryAssetLoader
NewMemoryAssetLoader creates a new MemoryAssetLoader.
func (*MemoryAssetLoader) Clear ¶
func (m *MemoryAssetLoader) Clear()
Clear removes all loaded assets.
func (*MemoryAssetLoader) FontCount ¶
func (m *MemoryAssetLoader) FontCount() int
FontCount returns the number of loaded fonts.
func (*MemoryAssetLoader) GetFont ¶
func (m *MemoryAssetLoader) GetFont(name string) ([]byte, bool)
GetFont retrieves a loaded font by name.
Returns the font data and true if found, or nil and false if not found.
func (*MemoryAssetLoader) GetIcon ¶
func (m *MemoryAssetLoader) GetIcon(name string) ([]byte, bool)
GetIcon retrieves a loaded icon by name.
Returns the icon data and true if found, or nil and false if not found.
func (*MemoryAssetLoader) GetImage ¶
func (m *MemoryAssetLoader) GetImage(name string) ([]byte, bool)
GetImage retrieves a loaded image by name.
Returns the image data and true if found, or nil and false if not found.
func (*MemoryAssetLoader) IconCount ¶
func (m *MemoryAssetLoader) IconCount() int
IconCount returns the number of loaded icons.
func (*MemoryAssetLoader) ImageCount ¶
func (m *MemoryAssetLoader) ImageCount() int
ImageCount returns the number of loaded images.
func (*MemoryAssetLoader) LoadFont ¶
func (m *MemoryAssetLoader) LoadFont(name string, data []byte) error
LoadFont implements AssetLoader.
type Plugin ¶
type Plugin interface {
// Name returns the unique identifier for this plugin.
//
// Plugin names should be lowercase, hyphen-separated, and unique
// within an application. Example: "material3", "corporate-design"
Name() string
// Version returns the semantic version of this plugin.
//
// Version strings should follow semantic versioning (semver):
// "MAJOR.MINOR.PATCH", e.g., "1.0.0", "2.1.3"
Version() string
// Dependencies returns the list of plugins this plugin depends on.
//
// The plugin manager will ensure all dependencies are initialized
// before this plugin's Init method is called. Return nil or an
// empty slice if the plugin has no dependencies.
Dependencies() []Dependency
// Init is called when the plugin is initialized.
//
// Use the provided [PluginContext] to register widgets, themes,
// layouts, and load assets. The context provides thread-safe
// access to all registries.
//
// Return an error if initialization fails. This will prevent
// the application from starting.
Init(ctx *PluginContext) error
// Shutdown is called when the plugin is unloaded.
//
// Use this method to release any resources allocated during Init.
// Plugins are shut down in reverse initialization order.
//
// Return an error if shutdown fails. All plugins will still be
// attempted to shut down even if one fails.
Shutdown() error
}
Plugin provides bundled UI components with lifecycle management.
A plugin is a cohesive unit that can register widgets, themes, layouts, and assets. Plugins support dependency declaration, allowing the plugin system to initialize them in the correct order.
Implement this interface to create a custom plugin:
type MyPlugin struct{}
func (p *MyPlugin) Name() string { return "my-plugin" }
func (p *MyPlugin) Version() string { return "1.0.0" }
func (p *MyPlugin) Dependencies() []Dependency {
return nil
}
func (p *MyPlugin) Init(ctx *PluginContext) error {
ctx.Widgets.Register("my-button", NewMyButton)
return nil
}
func (p *MyPlugin) Shutdown() error {
return nil
}
type PluginContext ¶
type PluginContext struct {
// Widgets provides access to the widget registry.
//
// Use this to register widget factories that create instances of
// your custom widgets.
Widgets *registry.WidgetRegistry
// Themes provides access to the theme registry.
//
// Use this to register themes that define colors, typography,
// spacing, and other visual properties.
Themes *theme.ThemeRegistry
// Layouts provides access to the layout registry.
//
// Use this to register custom layout algorithms that control
// how widgets are arranged.
Layouts *layout.Registry
// Assets provides methods for loading plugin resources.
//
// Use this to load fonts, icons, and images that your plugin
// provides.
Assets AssetLoader
}
PluginContext provides access to UI registries for plugin initialization.
When a plugin's Init method is called, it receives a PluginContext that provides access to the widget, theme, and layout registries, as well as an asset loader for fonts, icons, and images.
All registry operations are thread-safe, but plugins should still be careful about the order of registration to avoid dependencies on components that haven't been registered yet.
Example:
func (p *MyPlugin) Init(ctx *PluginContext) error {
// Register widgets
ctx.Widgets.Register("my-button", NewMyButton, registry.WidgetInfo{
Name: "my-button",
Description: "A custom button widget",
Category: registry.CategoryInput,
})
// Register themes
ctx.Themes.Register("my-theme", myTheme, theme.ThemeInfo{
Name: "My Theme",
Description: "A custom theme",
})
// Register layouts
ctx.Layouts.Register(&MyLayout{})
// Load assets
ctx.Assets.LoadFont("my-font", fontData)
ctx.Assets.LoadIcon("my-icon", iconData)
return nil
}
func NewDefaultPluginContext ¶
func NewDefaultPluginContext() *PluginContext
NewDefaultPluginContext creates a PluginContext with global registries.
This is the standard context used when initializing plugins through the global plugin manager.
func NewPluginContext ¶
func NewPluginContext( widgets *registry.WidgetRegistry, themes *theme.ThemeRegistry, layouts *layout.Registry, assets AssetLoader, ) *PluginContext
NewPluginContext creates a new PluginContext with the given registries.
If any registry is nil, the global registry for that type is used. If assets is nil, a no-op asset loader is used.
type PluginInfo ¶
type PluginInfo struct {
// Name is the unique identifier for the plugin.
// Should match Plugin.Name().
Name string
// Description is a brief description of what the plugin provides.
Description string
// Version is the semantic version of the plugin.
// Should match Plugin.Version().
Version string
// Author is the creator or maintainer of the plugin.
Author string
// License is the license under which the plugin is distributed.
// Example: "MIT", "Apache-2.0", "Proprietary"
License string
// Homepage is a URL to the plugin's homepage or repository.
Homepage string
// Dependencies lists the plugins this plugin depends on.
// This is populated from Plugin.Dependencies() during registration.
Dependencies []Dependency
}
PluginInfo describes metadata about a registered plugin.
PluginInfo provides human-readable information about a plugin, including its name, description, author, and license. This metadata can be used to build plugin management UIs or generate documentation.
Example:
info := PluginInfo{
Name: "material3",
Description: "Google Material Design 3 components",
Version: "1.0.0",
Author: "gogpu team",
License: "MIT",
Homepage: "https://github.com/gogpu/ui-material3",
}
func AllInfo ¶
func AllInfo() []PluginInfo
AllInfo returns information about all registered plugins.
func Info ¶
func Info(name string) (PluginInfo, bool)
Info returns information about a registered plugin.
type PluginManager ¶
type PluginManager struct {
// contains filtered or unexported fields
}
PluginManager manages plugin registration, initialization, and shutdown.
The plugin manager maintains a registry of plugins and handles their lifecycle. It resolves dependencies between plugins and initializes them in the correct order.
For most use cases, use the package-level functions (Register, Initialize, Shutdown, List, Info) which operate on a global plugin manager.
Create a custom PluginManager for testing or isolated use cases.
func GlobalManager ¶
func GlobalManager() *PluginManager
GlobalManager returns the global plugin manager.
This is useful for advanced use cases where direct access to the manager is needed.
func NewPluginManager ¶
func NewPluginManager() *PluginManager
NewPluginManager creates a new PluginManager.
The manager starts empty and uninitialized. Register plugins with PluginManager.Register and then call PluginManager.Initialize.
func (*PluginManager) AllInfo ¶
func (m *PluginManager) AllInfo() []PluginInfo
AllInfo returns information about all registered plugins.
The returned slice is sorted by plugin name.
func (*PluginManager) Clear ¶
func (m *PluginManager) Clear() error
Clear removes all registered plugins.
If plugins are initialized, they are shut down first. This is primarily useful for testing.
func (*PluginManager) Count ¶
func (m *PluginManager) Count() int
Count returns the number of registered plugins.
func (*PluginManager) Get ¶
func (m *PluginManager) Get(name string) (Plugin, bool)
Get returns a plugin by name.
Returns false as the second value if the plugin is not registered.
func (*PluginManager) Has ¶
func (m *PluginManager) Has(name string) bool
Has returns true if a plugin with the given name is registered.
func (*PluginManager) Info ¶
func (m *PluginManager) Info(name string) (PluginInfo, bool)
Info returns information about a registered plugin.
Returns false as the second value if the plugin is not registered.
func (*PluginManager) Initialize ¶
func (m *PluginManager) Initialize() error
Initialize loads all registered plugins in dependency order.
This method: 1. Validates all dependencies are registered 2. Checks version constraints 3. Resolves initialization order via topological sort 4. Calls Init() on each plugin in order
Returns an error if: - Already initialized (ErrAlreadyInitialized) - A dependency is not registered (ErrDependencyNotFound) - A version constraint is not satisfied (ErrVersionMismatch) - Circular dependencies are detected (ErrCircularDependency) - A plugin's Init() method returns an error
func (*PluginManager) InitializeWithContext ¶
func (m *PluginManager) InitializeWithContext(ctx *PluginContext) error
InitializeWithContext loads all plugins using a custom context.
This allows using custom registries or asset loaders instead of the global ones.
func (*PluginManager) IsInitialized ¶
func (m *PluginManager) IsInitialized() bool
IsInitialized returns true if plugins have been initialized.
func (*PluginManager) List ¶
func (m *PluginManager) List() []string
List returns the names of all registered plugins in sorted order.
func (*PluginManager) LoadOrder ¶
func (m *PluginManager) LoadOrder() []string
LoadOrder returns the order in which plugins were initialized.
Returns nil if plugins have not been initialized.
func (*PluginManager) MustRegister ¶
func (m *PluginManager) MustRegister(plugin Plugin, info ...PluginInfo)
MustRegister is like Register but panics if registration fails.
This is intended for use in init() functions where registration failure indicates a programming error.
func (*PluginManager) Register ¶
func (m *PluginManager) Register(plugin Plugin, info ...PluginInfo) error
Register adds a plugin to the manager.
The plugin's Name() method is used as the identifier. If info is provided, it stores metadata about the plugin for discovery purposes.
Returns ErrNilPlugin if plugin is nil, ErrEmptyName if the plugin's name is empty, or ErrPluginExists if a plugin with the same name is already registered.
Note: Registration does not initialize the plugin. Call Initialize after all plugins are registered.
func (*PluginManager) Shutdown ¶
func (m *PluginManager) Shutdown() error
Shutdown unloads all plugins in reverse initialization order.
This method calls Shutdown() on each plugin, starting with those that were initialized last (to respect dependency order).
Returns an error if plugins were not initialized, or if any plugin's Shutdown() method returns an error. All plugins are attempted to shut down even if some fail.