-
Notifications
You must be signed in to change notification settings - Fork 368
/
HtmlNotebookFrontendEnvironment.cs
113 lines (98 loc) · 4.17 KB
/
HtmlNotebookFrontendEnvironment.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Html;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Events;
using Microsoft.DotNet.Interactive.Formatting;
namespace Microsoft.DotNet.Interactive.Http;
public class HtmlNotebookFrontendEnvironment : BrowserFrontendEnvironment
{
private readonly TimeSpan _getApiUriTimeout;
private readonly TaskCompletionSource<Uri> _completionSource;
private readonly Dictionary<string, (KernelInvocationContext Context, TaskCompletionSource CompletionSource)> _tokenToInvocationContext;
public HtmlNotebookFrontendEnvironment(TimeSpan? apiUriTimeout = null)
{
RequiresAutomaticBootstrapping = true;
_completionSource = new TaskCompletionSource<Uri>();
_tokenToInvocationContext = new Dictionary<string, (KernelInvocationContext, TaskCompletionSource)>();
_getApiUriTimeout = apiUriTimeout ?? TimeSpan.FromSeconds(30);
}
public HtmlNotebookFrontendEnvironment(Uri apiUri) : this()
{
SetApiUri(apiUri);
}
public bool RequiresAutomaticBootstrapping { get; set; }
public void SetApiUri(Uri apiUri)
{
_completionSource.TrySetResult(apiUri);
}
public Task<Uri> GetApiUriAsync()
{
return _completionSource.Task;
}
public override async Task ExecuteClientScript(string code, KernelInvocationContext context)
{
var commandToken = context.Command.GetOrCreateToken();
var apiUriTask = GetApiUriAsync();
var completedTask = await Task.WhenAny(apiUriTask, Task.Delay(_getApiUriTimeout));
if (completedTask != apiUriTask)
{
throw new TimeoutException("Timeout resolving the kernel's HTTP endpoint. Please try again.");
}
var apiUri = apiUriTask.Result;
var codePrelude = $@"
if (typeof window.createDotnetInteractiveClient === typeof Function) {{
window.createDotnetInteractiveClient('{apiUri.AbsoluteUri}').then(async function (interactive) {{
const console = interactive.getConsole('{commandToken}');
const notebookScope = getDotnetInteractiveScope('{apiUri.AbsoluteUri}');
try {{
await Object.getPrototypeOf(async function() {{}}).constructor(
""interactive"",
""console"",
""notebookScope"",
""".Replace("\r\n", "\n");
var codePostlude = $@"""
)(
interactive,
console,
notebookScope
);
}} catch (err) {{
interactive.failCommand(err, '{commandToken}');
}} finally {{
await interactive.waitForAllEventsToPublish('{commandToken}');
await interactive.markExecutionComplete('{commandToken}');
}}
}});
}}
".Replace("\r\n", "\n");
var wrappedCode = $"{codePrelude}{HttpUtility.JavaScriptStringEncode(code)}{codePostlude}";
var executionCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_tokenToInvocationContext[commandToken] = (context, executionCompletionSource);
IHtmlContent content = PocketViewTags.script[type: "text/javascript"](wrappedCode.ToHtmlContent());
var formattedValues = FormattedValue.FromObject(content);
context.Publish(new DisplayedValueProduced(content, context.Command, formattedValues));
await executionCompletionSource.Task;
}
public void PublishEventFromCommand(string commandToken, Func<KernelCommand, KernelEvent> eventEnvelopeCreator)
{
if (_tokenToInvocationContext.TryGetValue(commandToken, out var item))
{
var kernelCommand = item.Context.Command;
var kernelEvent = eventEnvelopeCreator.Invoke(kernelCommand);
item.Context.Publish(kernelEvent);
}
}
public void MarkExecutionComplete(string token)
{
if (_tokenToInvocationContext.TryGetValue(token, out var item))
{
item.CompletionSource.SetResult();
}
_tokenToInvocationContext.Remove(token);
}
}