Gum gum-tool-plugins
Reference guide for the Gum tool's plugin system, including visualization plugins (EditorTabPlugin_XNA, TextureCoordinateSelectionPlugin). Load this when working on plugin registration, PluginBase, PriorityPlugin, PluginManager, plugin events, visualization/rendering concerns, or finding which internal plugin owns a feature.
git clone https://github.com/vchelaru/Gum
T=$(mktemp -d) && git clone --depth=1 https://github.com/vchelaru/Gum "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/gum-tool-plugins" ~/.claude/skills/vchelaru-gum-gum-tool-plugins && rm -rf "$T"
.claude/skills/gum-tool-plugins/SKILL.mdGum Tool Plugin System Reference
Architecture
The plugin system uses MEF (Managed Extensibility Framework) for discovery. All plugins are marked with
[Export(typeof(PluginBase))] and auto-discovered at startup.
Class Hierarchy
— minimal interface:IPlugin
,StartUp()
,ShutDown(PluginShutDownReason)
,FriendlyName
,UniqueIdVersion
— concrete base with all event declarations and pre-injected helper services (PluginBase
,_guiCommands
,_fileCommands
,_tabManager
,_menuStripManager
)_dialogService
— marker base for plugins that should receive events before others; provides defaultPriorityPlugin
returningShutDown()
and auto-generatesfalseFriendlyName
Origin vs. Priority
Origin (where the plugin's code lives) is independent of priority (whether it receives events early):
- First-party plugins live in
and are compiled into Gum.exe. Most inherit fromGum/Plugins/InternalPlugins/
.PriorityPlugin - External plugins are separate .dlls loaded from
at runtime. They usually inherit from[GumExecutableDirectory]\Plugins\
directly, but may inherit fromPluginBase
if they need early event dispatch (e.g.PriorityPlugin
, which ships as an external DLL but needs priority for wireframe events).EditorTabPlugin_XNA
The type check
is PriorityPlugin is used at runtime — priority plugins receive events before non-priority ones, regardless of origin.
Key Files
| File | Purpose |
|---|---|
| All event declarations + helper services |
| Marker base granting early event dispatch |
| Loads plugins via MEF, routes all events via methods |
| Wraps each plugin; tracks enabled state and failure info |
| All built-in plugin subfolders |
Plugin Lifecycle
StartUp() is called once on load — subscribe to events here. ShutDown(PluginShutDownReason) is called on unload. Service dependencies are injected via Locator.GetRequiredService<T>() (typically called in the constructor, not StartUp). If any plugin handler throws, PluginContainer disables that plugin for the rest of the session.
Internal Plugin Map
Each internal plugin lives in
Gum/Plugins/InternalPlugins/[FeatureName]/ with a Main[FeatureName]Plugin.cs entry point.
| Feature | Plugin Folder |
|---|---|
| Element tree view | |
| Variables/Properties tab | |
| State panel | |
| Behaviors panel | |
| Output panel | |
| Alignment controls | |
| Menu strip | |
| Undo/History | |
| Delete dialog | |
Common Events
All events are defined on
PluginBase — subscribe in StartUp(). The full list is in PluginBase.cs. Most-used categories:
- Selection:
,ElementSelected
,InstanceSelected
,ReactToStateSaveSelected
,BehaviorSelectedTreeNodeSelected - Variable changes:
,VariableSetVariableSetLate - Element lifecycle:
,ElementAdd
,ElementDelete
,ElementRename
,ElementDuplicateElementReloaded - Instance lifecycle:
,InstanceAdd
,InstanceDelete
,InstanceRenameInstanceReordered - Project:
,ProjectLoad
,BeforeProjectSaveAfterProjectSave - Wireframe:
,WireframeRefreshed
,BeforeRender
,AfterRenderCameraChanged
Query events (plugins return values to intercept behavior):
TryHandleDelete, GetSelectedIpsos, VariableExcluded, GetDeleteStateResponse, CreateGraphicalUiElement
Visualization Plugins
Visualization/rendering is handled by external plugin projects, not by Gum.csproj itself.
EditorTabPlugin_XNA (
Tool/EditorTabPlugin_XNA/) is the primary visualization plugin. It uses KNI (the runtime the Gum tool uses for rendering) and owns all runtime/rendering concerns: creating runtime instances for the wireframe preview, rendering, and wiring all CustomSetPropertyOnRenderable statics in its StartUp() method (SetPropertyOnRenderable, UpdateFontFromProperties, ThrowExceptionsForMissingFiles, AddRenderableToManagers, RemoveRenderableFromManagers, FontService, PropertyAssignmentError).
TextureCoordinateSelectionPlugin (
Gum/TextureCoordinateSelectionPlugin/) piggybacks on the statics that EditorTabPlugin_XNA sets up — it does not wire its own CustomSetPropertyOnRenderable statics.
Gum.csproj is save-class territory. It should operate purely on save classes (data model) without runtime/rendering dependencies. Runtime code that still exists in Gum.csproj (like
WireframeObjectManager) is legacy being actively refactored out to plugins. Do not add new runtime/rendering code to Gum.csproj.
Non-Obvious Behaviors
Event ordering:
PluginManager sorts with OrderBy(!(item is PriorityPlugin)), so priority plugins always handle events before non-priority ones. Note: "priority" is about dispatch order, not where the plugin's code lives — an external DLL can still be a PriorityPlugin.
VariableSet vs. VariableSetLate: Two events for the same change. Use
VariableSet to respond to a change; use VariableSetLate for cleanup/refresh that should run after all other plugins have responded.
Finding which plugin owns a feature: Search
StartUp() methods for the event subscription. E.g., to find what handles VariableSet, grep for VariableSet += in InternalPlugins/. The subscribing plugin is the owner.