Skip to content

Commit

Permalink
Merge pull request #23 from WolderLabs/cleanup
Browse files Browse the repository at this point in the history
Scaffold Interactive Web project
  • Loading branch information
rebelzach authored Mar 2, 2024
2 parents b8419cc + 2989afd commit 6e958f8
Show file tree
Hide file tree
Showing 27 changed files with 340 additions and 11 deletions.
2 changes: 1 addition & 1 deletion samples/FizzBuzz.Generator.Basic/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
await host.Services.GetRequiredService<GeneratorWorkspaceBuilder>()
.AddCommandLineActions()
.AddCSharpActions()
.InvokeAsync<CreateFizzBuzz>("FizzBuzz.Basic.Output");
.BuildWorkspaceAndRunAsync<CreateFizzBuzz>("FizzBuzz.Basic.Output");

class CreateFizzBuzz(
CommandLineActions commandLine,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<ProjectReference Include="..\..\src\Wolder.Core\Wolder.Core.csproj" />
<ProjectReference Include="..\..\src\Wolder.CSharp.OpenAI\Wolder.CSharp.OpenAI.csproj" />
<ProjectReference Include="..\..\src\Wolder.CSharp\Wolder.CSharp.csproj" />
<ProjectReference Include="..\..\src\Wolder.Interactive.Web\Wolder.Interactive.Web.csproj" />
<ProjectReference Include="..\..\src\Wolder.OpenAI\Wolder.OpenAI.csproj" />
</ItemGroup>

Expand Down
4 changes: 3 additions & 1 deletion samples/FizzBuzz.Generator.OpenAI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Wolder.CommandLine.Actions;
using Wolder.Core.Workspace;
using Wolder.CSharp.CodeActions;
using Wolder.Interactive.Web;

var builder = Host.CreateApplicationBuilder();
builder.Logging.AddConsole();
Expand All @@ -22,7 +23,8 @@
await host.Services.GetRequiredService<GeneratorWorkspaceBuilder>()
.AddCommandLineActions()
.AddCSharpGeneration()
.InvokeAsync<GenerateFizzBuzz>("FizzBuzz.OpenAI.Output");
.AddInteractiveWebServer()
.BuildWorkspaceAndRunAsync<GenerateFizzBuzz>("FizzBuzz.OpenAI.Output");

class GenerateFizzBuzz(
CommandLineActions commandLine,
Expand Down
2 changes: 1 addition & 1 deletion samples/TodoList.Blazor.Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
await host.Services.GetRequiredService<GeneratorWorkspaceBuilder>()
.AddCommandLineActions()
.AddCSharpGeneration()
.InvokeAsync<GenerateTodoListApp>("TodoList.Blazor.Output");
.BuildWorkspaceAndRunAsync<GenerateTodoListApp>("TodoList.Blazor.Output");

class GenerateTodoListApp(
CommandLineActions commandLine,
Expand Down
6 changes: 6 additions & 0 deletions src/Wolder.Core/Workspace/Events/InvocationBeginContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Wolder.Core.Workspace.Events;

public record InvocationBeginContext(
IInvokable Invokable, object? Parameter)
{
}
7 changes: 7 additions & 0 deletions src/Wolder.Core/Workspace/Events/InvocationEndContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Wolder.Core.Workspace.Events;

public record InvocationEndContext(
IInvokable Invokable)
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Wolder.Core.Workspace.Events;

public class WorkspaceStateEventDispatcher
{
internal WorkspaceStateEvents Events { get; }
public ICollection<WorkspaceStateEvents> Delegates { get; } = new List<WorkspaceStateEvents>();

public WorkspaceStateEventDispatcher()
{
Events = new WorkspaceStateEvents()
{
WorkspaceInitializedAsync = async () =>
await Task.WhenAll(Delegates.Select(d =>
d.WorkspaceInitializedAsync())),
WorkspaceRunEndAsync = async () =>
await Task.WhenAll(Delegates.Select(d =>
d.WorkspaceRunEndAsync())),
InvocationBeginAsync = async (c) =>
await Task.WhenAll(Delegates.Select(d =>
d.InvocationBeginAsync(c))),
InvocationEndAsync = async (c) =>
await Task.WhenAll(Delegates.Select(d =>
d.InvocationEndAsync(c))),
};
}
}
9 changes: 9 additions & 0 deletions src/Wolder.Core/Workspace/Events/WorkspaceStateEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Wolder.Core.Workspace.Events;

public class WorkspaceStateEvents
{
public Func<Task> WorkspaceInitializedAsync { get; set; } = () => Task.CompletedTask;
public Func<Task> WorkspaceRunEndAsync { get; set; } = () => Task.CompletedTask;
public Func<InvocationBeginContext, Task> InvocationBeginAsync { get; set; } = (c) => Task.CompletedTask;
public Func<InvocationEndContext, Task> InvocationEndAsync { get; set; } = (c) => Task.CompletedTask;
}
7 changes: 6 additions & 1 deletion src/Wolder.Core/Workspace/GeneratorWorkspaceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.Extensions.Logging;
using Wolder.Core.Assistants;
using Wolder.Core.Files;
using Wolder.Core.Workspace.Events;

namespace Wolder.Core.Workspace;

Expand All @@ -12,13 +13,15 @@ public class GeneratorWorkspaceBuilder
private readonly ILoggerFactory _loggerFactory;
private readonly IConfigurationSection _rootConfiguration;
private readonly ServiceCollection _services;
public WorkspaceStateEventDispatcher EventDispatcher { get; } = new();

public GeneratorWorkspaceBuilder(ILoggerFactory loggerFactory, IConfigurationSection rootConfiguration)
{
_loggerFactory = loggerFactory;
_rootConfiguration = rootConfiguration;
_services = new ServiceCollection();
_services.AddSingleton<WorkspaceRootPath>();
_services.AddSingleton(EventDispatcher);
_services.AddSingleton(loggerFactory);
_services.Add(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
_services.AddScoped<IInvoke, InvocationMiddleware>();
Expand All @@ -38,14 +41,16 @@ public GeneratorWorkspaceBuilder AddActions<TActionCollection>()
return this;
}

public async Task InvokeAsync<TRootAction>(string rootPath)
public async Task BuildWorkspaceAndRunAsync<TRootAction>(string rootPath)
where TRootAction : IVoidAction
{
var serviceProvider = _services.BuildServiceProvider();
var rootPathService = serviceProvider.GetRequiredService<WorkspaceRootPath>();
rootPathService.SetRootPath(rootPath);

await EventDispatcher.Events.WorkspaceInitializedAsync();
var invokeRootAction = serviceProvider.GetRequiredService<IInvoke>();
await invokeRootAction.InvokeVoidAsync<TRootAction>();
await EventDispatcher.Events.WorkspaceRunEndAsync();
}
}
15 changes: 11 additions & 4 deletions src/Wolder.Core/Workspace/IInvokable.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
namespace Wolder.Core.Workspace;

public interface IInvokable<in TParameter, TOutput>
public interface IInvokable<in TParameter, TOutput> : IInvokable
where TParameter : notnull
{
Task<TOutput> InvokeAsync();
}

public interface IInvokable<TOutput>
public interface IInvokable<TOutput> : IInvokable
{
Task<TOutput> InvokeAsync();
}

public interface IVoidInvokable<in TParameter>
public interface IVoidInvokable<in TParameter> : IInvokable
where TParameter : notnull
{
Task InvokeAsync();
}

public interface IVoidInvokable
public interface IVoidInvokable : IInvokable
{
Task InvokeAsync();
}

/// <summary>
/// Marker interface
/// </summary>
public interface IInvokable
{
}
19 changes: 16 additions & 3 deletions src/Wolder.Core/Workspace/InvocationMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Wolder.Core.Workspace.Events;

namespace Wolder.Core.Workspace;

internal class InvocationMiddleware(IServiceProvider provider) : IInvoke
{
private readonly WorkspaceStateEventDispatcher _stateDelegate = provider.GetRequiredService<WorkspaceStateEventDispatcher>();

public async Task InvokeVoidAsync<TInvokable>()
where TInvokable : IVoidInvokable
{
var scope = provider.CreateScope();
using var scope = provider.CreateScope();
var invokable = ActivatorUtilities.CreateInstance<TInvokable>(scope.ServiceProvider);
await _stateDelegate.Events.InvocationBeginAsync(new (invokable, null));
await invokable.InvokeAsync();
await _stateDelegate.Events.InvocationEndAsync(new (invokable));
}

public async Task InvokeVoidAsync<TInvokable, TParameter>(TParameter parameter)
Expand All @@ -18,7 +23,9 @@ public async Task InvokeVoidAsync<TInvokable, TParameter>(TParameter parameter)
{
var scope = provider.CreateScope();
var invokable = ActivatorUtilities.CreateInstance<TInvokable>(scope.ServiceProvider, parameter);
await _stateDelegate.Events.InvocationBeginAsync(new(invokable, parameter));
await invokable.InvokeAsync();
await _stateDelegate.Events.InvocationEndAsync(new(invokable));
}

public async Task<TOutput> InvokeAsync<TInvokable, TParameter, TOutput>(TParameter parameter)
Expand All @@ -27,14 +34,20 @@ public async Task<TOutput> InvokeAsync<TInvokable, TParameter, TOutput>(TParamet
{
var scope = provider.CreateScope();
var invokable = ActivatorUtilities.CreateInstance<TInvokable>(scope.ServiceProvider, parameter);
return await invokable.InvokeAsync();
await _stateDelegate.Events.InvocationBeginAsync(new(invokable, parameter));
var result = await invokable.InvokeAsync();
await _stateDelegate.Events.InvocationEndAsync(new(invokable));
return result;
}

public async Task<TOutput> InvokeAsync<TInvokable, TOutput>()
where TInvokable : IInvokable<TOutput>
{
var scope = provider.CreateScope();
var invokable = ActivatorUtilities.CreateInstance<TInvokable>(scope.ServiceProvider);
return await invokable.InvokeAsync();
await _stateDelegate.Events.InvocationBeginAsync(new(invokable, null));
var result = await invokable.InvokeAsync();
await _stateDelegate.Events.InvocationEndAsync(new(invokable));
return result;
}
}
18 changes: 18 additions & 0 deletions src/Wolder.Interactive.Web/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="/"/>
<link rel="stylesheet" href="Wolder.Interactive.Web.styles.css"/>
<link rel="icon" type="image/png" href="favicon.png"/>
<HeadOutlet/>
</head>

<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>

</html>
3 changes: 3 additions & 0 deletions src/Wolder.Interactive.Web/Components/Component1.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="my-component">
This component is defined in the <strong>Wolder.Interactive.Web</strong> library.
</div>
6 changes: 6 additions & 0 deletions src/Wolder.Interactive.Web/Components/Component1.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.my-component {
border: 2px dashed red;
padding: 1em;
margin: 1em 0;
background-image: url('background.png');
}
18 changes: 18 additions & 0 deletions src/Wolder.Interactive.Web/Components/Index.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@page "/"
@using Wolder.Interactive.Web.Services

@inject WorkspaceStateManager WorkspaceStateManager;

@rendermode InteractiveServer

<h3>Wolder Interactive</h3>

<div>
<button @onclick="WorkspaceStateManager.Pause">Pause</button>
<button @onclick="WorkspaceStateManager.Resume">Resume</button>
<button @onclick="WorkspaceStateManager.Step">Step</button>
</div>

@code {

}
9 changes: 9 additions & 0 deletions src/Wolder.Interactive.Web/Components/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@inherits LayoutComponentBase

<main>
@Body
</main>

@code {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

main {
font-family: monospace;
text-align: center;
}
6 changes: 6 additions & 0 deletions src/Wolder.Interactive.Web/Components/Routes.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
</Found>
</Router>
3 changes: 3 additions & 0 deletions src/Wolder.Interactive.Web/Models/InvocationDetail.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Wolder.Interactive.Web.Models;

public record InvocationDetail(string Name);
65 changes: 65 additions & 0 deletions src/Wolder.Interactive.Web/Services/WorkspaceStateManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Wolder.Core.Workspace.Events;
using Wolder.Interactive.Web.Models;

namespace Wolder.Interactive.Web.Services;

public class WorkspaceStateManager
{
private TaskCompletionSource _proceedWhenUnpaused = new(); // Start paused

public WorkspaceStateManager()
{
Events = new()
{
WorkspaceInitializedAsync = WorkspaceInitializedAsync,
WorkspaceRunEndAsync = WorkspaceRunEndAsync,
InvocationBeginAsync = InvocationBeginAsync,
InvocationEndAsync = InvocationEndAsync,
};
}

internal WorkspaceStateEvents Events { get; }

public void Pause()
{
_proceedWhenUnpaused = new TaskCompletionSource();
}

public void Resume()
{
_proceedWhenUnpaused.TrySetResult();
}

public void Step()
{
var prevTcs = _proceedWhenUnpaused;
_proceedWhenUnpaused = new TaskCompletionSource();
prevTcs.TrySetResult();
}

public event Action<InvocationDetail>? InvocationBegin;
public event Action? WorkspaceInitialized;

private async Task WorkspaceInitializedAsync()
{
WorkspaceInitialized?.Invoke();
await _proceedWhenUnpaused.Task;
}

private async Task InvocationBeginAsync(InvocationBeginContext c)
{
InvocationBegin?.Invoke(new InvocationDetail(c.Invokable.GetType().Name));
await _proceedWhenUnpaused.Task;
}

private async Task WorkspaceRunEndAsync()
{
Pause();
await _proceedWhenUnpaused.Task;
}

private Task InvocationEndAsync(InvocationEndContext c)
{
return Task.CompletedTask;
}
}
15 changes: 15 additions & 0 deletions src/Wolder.Interactive.Web/SetupExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Wolder.Core.Workspace;

namespace Wolder.Interactive.Web;

public static class GeneratorWorkspaceBuilderExtensions
{
public static GeneratorWorkspaceBuilder AddInteractiveWebServer(this GeneratorWorkspaceBuilder builder)
{
var server = new WorkspaceInteractiveWebHost();
builder.Services.AddSingleton(server);
builder.EventDispatcher.Delegates.Add(server.WorkspaceStateManager.Events);
return builder;
}
}
Loading

0 comments on commit 6e958f8

Please sign in to comment.