The extensibility mechanisms
These guides are written in English. · Building with an AI assistant?
Point your LLM at the docs so it understands them faster — it can also explain or translate the guides for you. Load the full documentation as a single file, or add any page's Markdown by appending .md to its URL. Works with Cursor (Add Docs), Claude, ChatGPT, Copilot and other LLMs.
Each mechanism is illustrated below with the GeneXus Work With pattern. Full, build-it
guides (interfaces, invocation flow, gotchas) live in the projects' ai/ folders.
1. Custom Types — dynamic dropdowns
You implement IPatternCustomTypeSupport.getTypeEditor(id), returning an editor whose
getValues(context) computes the options for a custom(<id>) property.
Work With example: the GridCustomRender custom type returns the available grid controls.
class WorkWithCustomTypeSupport implements IPatternCustomTypeSupport {
getTypeEditor(id: string) {
return id === 'GridCustomRender' ? new GridCustomRenderEditor() : null;
}
}
→ Deep dive: ai/EXTENSIBILITY-CUSTOM-TYPES.md.
2. Captions — node labels
You implement IPatternEditorHelper.customShowElement(element, ctx), returning
{ handled, caption, icon }. Return { handled: false } for nodes you don't customize.
(The optional icon lets you override a node's icon inline; for icons that ship as bundled
resources use the dedicated hook in mechanism 4.)
Work With example: the Modes node shows modes (Insert, Update, ...) listing the enabled
modes.
customShowElement(element) {
if (element.elementType === 'Modes')
return { handled: true, caption: `modes (${enabledModes(element).join(', ')})` };
return { handled: false };
}
→ Deep dive: ai/EXTENSIBILITY-CAPTIONS.md.
3. Custom Actions — context commands
You implement IPatternEditorHelper.getCommands(element), returning serializable commands
(usually subclasses of PatternEditorCommandBase). The command's exec() runs on click and
may mutate the instance tree.
Work With example: "Add Filter Variable" on the FilterAttributes node.
getCommands(element) {
return element.elementType === 'FilterAttributes'
? [new AddFilterVariableCommand(element).toSerializable()].filter(Boolean)
: [];
}
→ Deep dive: ai/EXTENSIBILITY-CUSTOM-ACTIONS.md.
4. Node icons — bundled resource icons
You implement IPatternEditorHelper.getNodeIcon(element, iconName?), returning the node's
icon as a self-contained resource (typically a data: URI). KB Editor resolves a node's icon
in order — the caption's inline icon (mechanism 2), then the schema's Icon when it exists
as a file next to the pattern, then this hook, then a built-in glyph — so returning undefined
falls through to the next source.
Work With example: the pattern declares its icons as .NET assembly resources
(Icon="ObjectAttribute" IconResource="…,Artech.Patterns.WorkWith"), which don't exist as
files on the client's KB. The extension bundles those images under icons/ (named exactly like
the schema Icon values) and returns them here, so the tree shows the real icons with no
GeneXus SDK or .NET assemblies at runtime.
getNodeIcon(element, iconName) {
if (!iconName || !/^[A-Za-z0-9_]+$/.test(iconName)) return undefined;
const file = path.join(this.iconsDir, `${iconName}.ico`);
return fs.existsSync(file)
? `data:image/x-icon;base64,${fs.readFileSync(file).toString('base64')}`
: undefined;
}
→ Deep dive: the Work With example's WorkWithEditorHelper.getNodeIcon and its bundled
icons/ folder.
Optional: Variables
If your pattern exposes context variables in embedded code, implement
IPatternVariableProvider to feed KB Editor's autocomplete. → ai/EXTENSIBILITY-VARIABLES.md.
Next: C# → TypeScript workflow.