diff --git a/src/web/modules/Flowsharp.Web.Abstractions/Drivers/TypedActivityDisplayDriver.cs b/src/web/modules/Flowsharp.Web.Abstractions/Drivers/TypedActivityDisplayDriver.cs
new file mode 100644
index 000000000..f9693d30a
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.Abstractions/Drivers/TypedActivityDisplayDriver.cs
@@ -0,0 +1,20 @@
+using Flowsharp.Models;
+
+namespace Flowsharp.Web.Abstractions.Drivers
+{
+ ///
+ /// Base class for activity drivers.
+ ///
+ public abstract class TypedActivityDisplayDriver : ActivityDisplayDriver where TActivity : class, IActivity
+ {
+ public override bool CanHandleModel(IActivity model) => model.Name == typeof(TActivity).Name;
+ }
+
+ ///
+ /// Base class for activity drivers using a strongly typed view model.
+ ///
+ public abstract class TypedActivityDisplayDriver : ActivityDisplayDriver where TActivity : class, IActivity where TEditViewModel : class, new()
+ {
+ public override bool CanHandleModel(IActivity model) => model.Name == typeof(TActivity).Name;
+ }
+}
\ No newline at end of file
diff --git a/src/web/modules/Flowsharp.Web.Abstractions/Services/IActivityDisplayManager.cs b/src/web/modules/Flowsharp.Web.Abstractions/Services/IActivityDisplayManager.cs
new file mode 100644
index 000000000..00f8143f9
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.Abstractions/Services/IActivityDisplayManager.cs
@@ -0,0 +1,9 @@
+using Flowsharp.Models;
+using OrchardCore.DisplayManagement;
+
+namespace Flowsharp.Web.Abstractions.Services
+{
+ public interface IActivityDisplayManager : IDisplayManager
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/web/modules/Flowsharp.Web.Abstractions/ViewModels/ActivityDescriptorViewModel.cs b/src/web/modules/Flowsharp.Web.Abstractions/ViewModels/ActivityDescriptorViewModel.cs
new file mode 100644
index 000000000..c568c2bd5
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.Abstractions/ViewModels/ActivityDescriptorViewModel.cs
@@ -0,0 +1,21 @@
+using Flowsharp.Models;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using OrchardCore.DisplayManagement.Views;
+
+namespace Flowsharp.Web.Abstractions.ViewModels
+{
+ public class ActivityViewModel : ShapeViewModel
+ {
+ public ActivityViewModel()
+ {
+ }
+
+ public ActivityViewModel(IActivity activity)
+ {
+ Activity = activity;
+ }
+
+ [BindNever]
+ public IActivity Activity { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/web/modules/Flowsharp.Web.ViewComponents/Assets/scripts/flowsharp/workflow-designer/activity-editor.ts b/src/web/modules/Flowsharp.Web.ViewComponents/Assets/scripts/flowsharp/workflow-designer/activity-editor.ts
new file mode 100644
index 000000000..3bd780a94
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.ViewComponents/Assets/scripts/flowsharp/workflow-designer/activity-editor.ts
@@ -0,0 +1,40 @@
+///
+///
+
+namespace Flowsharp {
+
+ export class ActivityEditor {
+ public show = () => this.containerElement.modal('show');
+ public hide = () => this.containerElement.modal('hide');
+ public display = (activityName: string) => $.ajax({
+ type: 'POST',
+ url: `/activity/display/${activityName}`,
+ data: {},
+ contentType: 'application/json',
+ dataType: 'json'
+ }).done(this.displayActivity);
+ private baseUrl: string;
+ private containerElement: JQuery;
+ private titleElement: JQuery;
+ private contentElement: JQuery;
+ private displayActivity = (data: any) => {
+ const html = data;
+ this.contentElement.html(html);
+ };
+
+ constructor(container: HTMLElement) {
+ this.containerElement = $(container);
+ this.titleElement = this.containerElement.find('.modal-title');
+ this.contentElement = this.containerElement.find('.modal-body');
+ this.baseUrl = this.containerElement.data('base-url');
+ }
+
+ public get title(): string {
+ return this.titleElement.text();
+ }
+
+ public set title(value: string) {
+ this.titleElement.text(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/web/modules/Flowsharp.Web.ViewComponents/Controllers/ActivityController.cs b/src/web/modules/Flowsharp.Web.ViewComponents/Controllers/ActivityController.cs
new file mode 100644
index 000000000..84ab24319
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.ViewComponents/Controllers/ActivityController.cs
@@ -0,0 +1,32 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Flowsharp.Models;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json.Linq;
+using OrchardCore.DisplayManagement;
+using OrchardCore.DisplayManagement.ModelBinding;
+
+namespace Flowsharp.Web.ViewComponents.Controllers
+{
+ [Route("[controller]")]
+ [IgnoreAntiforgeryToken]
+ public class ActivityController : Controller, IUpdateModel
+ {
+ private readonly IDisplayManager displayManager;
+ private readonly IActivityLibrary activityLibrary;
+
+ public ActivityController(IDisplayManager displayManager, IActivityLibrary activityLibrary)
+ {
+ this.displayManager = displayManager;
+ this.activityLibrary = activityLibrary;
+ }
+
+ [HttpPost("display/{activityName}")]
+ public async Task Display(string activityName, [FromBody] JToken json, CancellationToken cancellationToken)
+ {
+ var activity = await activityLibrary.GetActivityByNameAsync(activityName, cancellationToken);
+ var editorShape = await displayManager.BuildEditorAsync(activity, this, true);
+ return View(editorShape);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/web/modules/Flowsharp.Web.ViewComponents/Services/ActivityDisplayManager.cs b/src/web/modules/Flowsharp.Web.ViewComponents/Services/ActivityDisplayManager.cs
new file mode 100644
index 000000000..f1907923f
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.ViewComponents/Services/ActivityDisplayManager.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using Flowsharp.Models;
+using Flowsharp.Web.Abstractions.Services;
+using Microsoft.Extensions.Logging;
+using OrchardCore.DisplayManagement;
+using OrchardCore.DisplayManagement.Descriptors;
+using OrchardCore.DisplayManagement.Handlers;
+using OrchardCore.DisplayManagement.Layout;
+using OrchardCore.DisplayManagement.Theming;
+
+namespace Flowsharp.Web.ViewComponents.Services
+{
+ public class ActivityDisplayManager : DisplayManager, IActivityDisplayManager
+ {
+ public ActivityDisplayManager(
+ IEnumerable> drivers,
+ IShapeTableManager shapeTableManager,
+ IShapeFactory shapeFactory,
+ IThemeManager themeManager,
+ ILogger> logger,
+ ILayoutAccessor layoutAccessor) : base(drivers, shapeTableManager, shapeFactory, themeManager, logger, layoutAccessor)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/web/modules/Flowsharp.Web.ViewComponents/Views/Activity/Display.cshtml b/src/web/modules/Flowsharp.Web.ViewComponents/Views/Activity/Display.cshtml
new file mode 100644
index 000000000..e92f063d5
--- /dev/null
+++ b/src/web/modules/Flowsharp.Web.ViewComponents/Views/Activity/Display.cshtml
@@ -0,0 +1,2 @@
+@model dynamic
+@await DisplayAsync(Model)
\ No newline at end of file