Flow Studio
Widget Registry
IWidgetRegistry — how widgets are registered, discovered by the designer, and how the WorkDesk loads widget bundles at runtime.
IWidgetDefinition Interface
// ProcessEngine/Widgets/IWidgetDefinition.cs
public interface IWidgetDefinition
{
string WidgetId { get; }
string DisplayName { get; }
string Description { get; }
string BundleUrl { get; } // URL of the frontend JS bundle
string PropsSchema { get; } // JSON Schema for widgetProps
string InteractionSchema { get; } // JSON Schema for interaction event data
IReadOnlyList<string> InteractionEvents { get; }
WidgetSandboxPolicy SandboxPolicy { get; }
}
IWidgetRegistry
public interface IWidgetRegistry
{
IWidgetDefinition? GetWidget(string widgetId);
IReadOnlyList<IWidgetDefinition> GetAllWidgets();
bool IsRegistered(string widgetId);
}
Platform Widget Definitions
// DataApprovalWidgetDefinition.cs
public class DataApprovalWidgetDefinition : IWidgetDefinition
{
public string WidgetId => "data-approval-widget";
public string DisplayName => "Data Approval Widget";
public string Description => "Review and approve/reject tabular data items";
public string BundleUrl => "https://cdn.bizfirstai.com/widgets/data-approval-widget/v2/bundle.js";
public string PropsSchema => @"{
type: 'object',
required: ['items'],
properties: {
items: { type: 'array' },
totalAmount: { type: 'number' },
currency: { type: 'string' },
readOnly: { type: 'boolean' }
}
}";
public IReadOnlyList<string> InteractionEvents => ["approve", "reject", "requestAmendment"];
}
Runtime Bundle Loading
When the WorkDesk loads a widget task, it uses the BundleUrl from the registry to dynamically load the widget's JavaScript bundle. The bundle exposes a standard entry point that the WidgetRenderer calls:
// The widget bundle exposes:
window.BizFirstWidgets['data-approval-widget'] = {
mount(container: HTMLElement, props: Record<string, unknown>, callbacks: WidgetCallbacks) {
// render the widget into container
// call callbacks.onInteraction(event, data) when user interacts
},
unmount(container: HTMLElement) {
// cleanup
}
};