microsoft/TypeAgent
Publicmirrored fromhttps://github.com/microsoft/TypeAgentAvailable
dotnet/visualStudioTypeAgent/ChatToolWindowControl.xaml.cs
82lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | using System; |
| 5 | using System.Diagnostics; |
| 6 | using System.IO; |
| 7 | using System.Reflection; |
| 8 | using System.Windows.Controls; |
| 9 | using Microsoft.VisualStudio.Shell; |
| 10 | using Microsoft.VisualStudio.Threading; |
| 11 | using Microsoft.Web.WebView2.Core; |
| 12 | using Newtonsoft.Json.Linq; |
| 13 | |
| 14 | namespace Microsoft.TypeAgent.VisualStudio; |
| 15 | |
| 16 | public partial class ChatToolWindowControl : UserControl |
| 17 | { |
| 18 | public ChatToolWindowControl() |
| 19 | { |
| 20 | InitializeComponent(); |
| 21 | Loaded += OnControlLoaded; |
| 22 | } |
| 23 | |
| 24 | private void OnControlLoaded(object sender, System.Windows.RoutedEventArgs e) |
| 25 | { |
| 26 | // Hand the async work to the JTF and observe the JoinableTask via |
| 27 | // FileAndForget so VSTHRD101/VSTHRD110 don't fire. |
| 28 | #pragma warning disable VSSDK007 // FileAndForget is the documented fire-and-forget observation for JoinableTask. |
| 29 | ThreadHelper.JoinableTaskFactory.RunAsync(async () => |
| 30 | { |
| 31 | await InitializeWebViewAsync(); |
| 32 | }).FileAndForget("typeagent/vsix/webview-init"); |
| 33 | #pragma warning restore VSSDK007 |
| 34 | } |
| 35 | |
| 36 | private async System.Threading.Tasks.Task InitializeWebViewAsync() |
| 37 | { |
| 38 | // User-data dir lives next to the extension assembly so multiple VS instances |
| 39 | // share cookies/cache. Tweak if you need per-instance isolation. |
| 40 | var userDataDir = Path.Combine(Path.GetTempPath(), "typeagent-vsix-webview"); |
| 41 | Directory.CreateDirectory(userDataDir); |
| 42 | |
| 43 | var env = await CoreWebView2Environment.CreateAsync(null, userDataDir); |
| 44 | await ChatWebView.EnsureCoreWebView2Async(env); |
| 45 | |
| 46 | // Map the bundled WebView2 content to a virtual host so we can use |
| 47 | // simple relative URLs and avoid the file:// CSP weirdness. |
| 48 | var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; |
| 49 | var contentDir = Path.Combine(assemblyDir, "webview-content"); |
| 50 | ChatWebView.CoreWebView2.SetVirtualHostNameToFolderMapping( |
| 51 | "typeagent.local", |
| 52 | contentDir, |
| 53 | CoreWebView2HostResourceAccessKind.Allow); |
| 54 | |
| 55 | // The WebView2 → host channel: chat-ui posts JSON for link clicks etc. |
| 56 | ChatWebView.CoreWebView2.WebMessageReceived += OnWebMessageReceived; |
| 57 | |
| 58 | ChatWebView.CoreWebView2.Navigate("https://typeagent.local/index.html"); |
| 59 | } |
| 60 | |
| 61 | private void OnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e) |
| 62 | { |
| 63 | try |
| 64 | { |
| 65 | var root = JObject.Parse(e.WebMessageAsJson); |
| 66 | switch (root.Value<string>("type")) |
| 67 | { |
| 68 | case "openExternal": |
| 69 | var url = root.Value<string>("url"); |
| 70 | if (!string.IsNullOrWhiteSpace(url)) |
| 71 | { |
| 72 | Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); |
| 73 | } |
| 74 | break; |
| 75 | } |
| 76 | } |
| 77 | catch (Exception ex) |
| 78 | { |
| 79 | Debug.WriteLine($"[TypeAgent] WebMessageReceived parse failed: {ex.Message}"); |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | |