36 | Current count: @currentCount 37 |
38 | 39 | 40 | 41 | @code { 42 | public int currentCount = 0; 43 | 44 | void IncrementCount() 45 | { 46 | currentCount++; 47 | } 48 | } 49 | 50 | #!csharp 51 | 52 | var componentName = typeof(Counter).Name; 53 | componentName 54 | -------------------------------------------------------------------------------- /src/BlazorInteractive/BlazorKernelExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Html; 2 | using Microsoft.DotNet.Interactive; 3 | using Microsoft.DotNet.Interactive.Commands; 4 | using Microsoft.DotNet.Interactive.Formatting; 5 | 6 | namespace BlazorInteractive; 7 | 8 | public sealed class BlazorKernelExtension : IKernelExtension, IStaticContentSource 9 | { 10 | public string Name => "Blazor"; 11 | 12 | public async Task OnLoadAsync(Kernel kernel) 13 | { 14 | if (kernel is not CompositeKernel compositeKernel) 15 | { 16 | throw new InvalidOperationException("The Blazor kernel can only be added into a CompositeKernel."); 17 | } 18 | 19 | // Add a BlazorKernel as a child kernel to the CompositeKernel 20 | compositeKernel.Add(new BlazorKernel()); 21 | 22 | await compositeKernel 23 | .UseBlazor() 24 | .LoadRequiredAssemblies(); 25 | 26 | var message = new HtmlString( 27 | """ 28 |This extension adds a new kernel that can render Blazor markdown.
31 | 32 |
33 |
34 | #!blazor
35 | Counter
36 |
37 |
38 | Current count: @currentCount
39 |
40 |
41 | @code {
42 | int currentCount = 0;
43 | }
44 |
45 |
46 | This extension also adds the compiled component as a type to the interactive workspace.
47 | 48 |Options:
49 |__Main48 | Current count: @currentCount 49 |
50 | 51 | 52 | 53 | @code { 54 | public int currentCount = 0; 55 | 56 | void IncrementCount() 57 | { 58 | currentCount++; 59 | } 60 | } 61 | ``` 62 | 63 | You can then use the component as any other class in the next code cells: 64 | 65 | ```csharp 66 | var componentName = typeof(Counter).Name; 67 | componentName 68 | ``` 69 | 70 | ```csharp 71 | var counter = new Counter(); 72 | counter.currentCount 73 | ``` 74 | 75 | ## Sequence of commands and events 76 | 77 | Here is an overview of the sequence of commands and events: 78 | 79 |  80 | 81 | ## Contributing 82 | 83 | Contributions are always welcome! 84 | 85 | ### How to compile this project 86 | 87 | Since this project requires a git submodule, you'll need to initialize and update the [Blazor REPL](https://github.com/BlazorRepl/BlazorRepl) submodule. 88 | 89 | #### On the first `git clone`: 90 | 91 | ``` 92 | git clone --recurse-submodules -j8 https://github.com/plbonneville/BlazorInteractive.git 93 | ``` 94 | 95 | #### If you already have the repository cloned, run: 96 | 97 | ``` 98 | git submodule init 99 | git submodule update 100 | ``` 101 | 102 | ## Built with 103 | 104 | - [Blazor REPL](https://github.com/BlazorRepl/BlazorRepl) 105 | - [bUnit](https://github.com/bUnit-dev/bUnit) 106 | - [.NET Interactive ](https://github.com/dotnet/interactive) 107 | -------------------------------------------------------------------------------- /src/BlazorInteractive/BlazorKernel.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using System.CommandLine.Invocation; 3 | using System.CommandLine.NamingConventionBinder; 4 | using System.CommandLine.Parsing; 5 | 6 | using Microsoft.DotNet.Interactive; 7 | using Microsoft.DotNet.Interactive.Commands; 8 | 9 | namespace BlazorInteractive; 10 | 11 | ///This extension adds a new kernel that can render Blazor markdown.
\r\n", 78 | "\r\n", 79 | "\r\n",
80 | " \r\n",
81 | "#!blazor\r\n",
82 | "Counter
\r\n",
83 | "\r\n",
84 | "\r\n",
85 | " Current count: @currentCount\r\n",
86 | "
\r\n",
87 | "\r\n",
88 | "@code {\r\n",
89 | " int currentCount = 0;\r\n",
90 | "}\r\n",
91 | " \r\n",
92 | "\r\n",
93 | " This extension also adds the compiled component as a type to the interactive workspace.
\r\n", 94 | "\r\n", 95 | "Options:
\r\n", 96 | "__Main\n", 154 | " Current count: 0
\n", 155 | "\n", 156 | "\n", 168 | " Current count: @currentCount\n", 169 | "
\n", 170 | "\n", 171 | "\n", 172 | "\n", 173 | "@code {\n", 174 | " public int currentCount = 0;\n", 175 | "\n", 176 | " void IncrementCount()\n", 177 | " {\n", 178 | " currentCount++;\n", 179 | " }\n", 180 | "}" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 5, 186 | "metadata": { 187 | "dotnet_interactive": { 188 | "language": "csharp" 189 | } 190 | }, 191 | "outputs": [ 192 | { 193 | "data": { 194 | "text/plain": [ 195 | "Counter" 196 | ] 197 | }, 198 | "metadata": {}, 199 | "output_type": "display_data" 200 | } 201 | ], 202 | "source": [ 203 | "var componentName = typeof(Counter).Name;\n", 204 | "componentName" 205 | ] 206 | } 207 | ], 208 | "metadata": { 209 | "kernelspec": { 210 | "display_name": ".NET (C#)", 211 | "language": "C#", 212 | "name": ".net-csharp" 213 | }, 214 | "language_info": { 215 | "name": "polyglot-notebook" 216 | }, 217 | "polyglot_notebook": { 218 | "kernelInfo": { 219 | "defaultKernelName": "csharp", 220 | "items": [ 221 | { 222 | "aliases": [], 223 | "name": ".NET" 224 | }, 225 | { 226 | "aliases": [], 227 | "name": "blazor" 228 | }, 229 | { 230 | "aliases": [ 231 | "C#", 232 | "c#" 233 | ], 234 | "languageName": "C#", 235 | "name": "csharp" 236 | }, 237 | { 238 | "aliases": [ 239 | "F#", 240 | "f#" 241 | ], 242 | "languageName": "F#", 243 | "name": "fsharp" 244 | }, 245 | { 246 | "aliases": [], 247 | "languageName": "HTML", 248 | "name": "html" 249 | }, 250 | { 251 | "aliases": [], 252 | "languageName": "KQL", 253 | "name": "kql" 254 | }, 255 | { 256 | "aliases": [], 257 | "languageName": "Mermaid", 258 | "name": "mermaid" 259 | }, 260 | { 261 | "aliases": [ 262 | "powershell" 263 | ], 264 | "languageName": "PowerShell", 265 | "name": "pwsh" 266 | }, 267 | { 268 | "aliases": [], 269 | "languageName": "SQL", 270 | "name": "sql" 271 | }, 272 | { 273 | "aliases": [], 274 | "name": "value" 275 | }, 276 | { 277 | "aliases": [ 278 | "frontend" 279 | ], 280 | "name": "vscode" 281 | } 282 | ] 283 | } 284 | } 285 | }, 286 | "nbformat": 4, 287 | "nbformat_minor": 2 288 | } 289 | -------------------------------------------------------------------------------- /tests/BlazorInteractive.Tests/BlazorInteractiveTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.DotNet.Interactive; 3 | using Microsoft.DotNet.Interactive.Commands; 4 | using Microsoft.DotNet.Interactive.CSharp; 5 | using Microsoft.DotNet.Interactive.Events; 6 | using Microsoft.DotNet.Interactive.FSharp; 7 | using Microsoft.DotNet.Interactive.Tests.Utility; 8 | using Xunit; 9 | 10 | namespace BlazorInteractive.Tests; 11 | 12 | public sealed class BlazorInteractiveTests : IDisposable 13 | { 14 | private readonly Kernel _kernel; 15 | 16 | public BlazorInteractiveTests() 17 | { 18 | // Prevent SynchronizationContext-induced deadlocks given the following sync-over-async code. 19 | ExecutionContext.SuppressFlow(); 20 | 21 | try 22 | { 23 | _kernel = new CompositeKernel 24 | { 25 | new CSharpKernel().UseNugetDirective(), 26 | new FSharpKernel().UseNugetDirective() 27 | }; 28 | 29 | Task.Run(async () => 30 | { 31 | var extension = new BlazorKernelExtension(); 32 | await extension.OnLoadAsync(_kernel); 33 | }) 34 | .Wait(); 35 | 36 | KernelEvents = _kernel.KernelEvents.ToSubscribedList(); 37 | } 38 | finally 39 | { 40 | ExecutionContext.RestoreFlow(); 41 | } 42 | } 43 | 44 | private SubscribedList136 | Current count: @currentCount 137 |
138 | 139 | 140 | 141 | @code { 142 | int currentCount = 0; 143 | 144 | void IncrementCount() 145 | { 146 | currentCount++; 147 | } 148 | } 149 | """; 150 | 151 | using var events = _kernel.KernelEvents.ToSubscribedList(); 152 | 153 | // Act 154 | await _kernel.SubmitCodeAsync(code); 155 | 156 | // Assert 157 | KernelEvents 158 | .Should() 159 | .ContainSingle