diff --git a/MonacoRoslynCompletionProvider/Api/CodeRunner.cs b/MonacoRoslynCompletionProvider/Api/CodeRunner.cs index 7931566..7b1e556 100644 --- a/MonacoRoslynCompletionProvider/Api/CodeRunner.cs +++ b/MonacoRoslynCompletionProvider/Api/CodeRunner.cs @@ -179,7 +179,8 @@ private static SyntaxTree CreateConsoleReadKeyShim(CSharpParseOptions parseOptio return CSharpSyntaxTree.ParseText( ConsoleReadKeyShimSource, parseOptions, - path: "__ConsoleReadKeyShim.cs"); + path: "__ConsoleReadKeyShim.cs", + encoding: Encoding.UTF8); } private static Task RunEntryPointAsync(Func executeAsync, bool requiresStaThread) diff --git a/SharpPad.Desktop/Interop/WebViewBridge.cs b/SharpPad.Desktop/Interop/WebViewBridge.cs index a68a487..950bbe4 100644 --- a/SharpPad.Desktop/Interop/WebViewBridge.cs +++ b/SharpPad.Desktop/Interop/WebViewBridge.cs @@ -66,11 +66,15 @@ private async void OnWebMessageReceived(object? sender, WebViewMessageReceivedEv case "pick-and-upload": await HandlePickAndUploadAsync(root); break; - + case "download-file": await HandleDownloadFileAsync(root); break; + case "open-external-url": + HandleOpenExternalUrl(root); + break; + default: Send(new { @@ -215,6 +219,58 @@ private async Task HandleDownloadFileAsync(JsonElement root) }); } + private void HandleOpenExternalUrl(JsonElement root) + { + if (!root.TryGetProperty("url", out var urlProperty) || + urlProperty.ValueKind != JsonValueKind.String) + { + Send(new + { + type = "open-external-url-result", + success = false, + message = "URL无效。" + }); + return; + } + + var url = urlProperty.GetString(); + if (string.IsNullOrWhiteSpace(url)) + { + Send(new + { + type = "open-external-url-result", + success = false, + message = "URL为空。" + }); + return; + } + + try + { + var psi = new System.Diagnostics.ProcessStartInfo + { + FileName = url, + UseShellExecute = true + }; + System.Diagnostics.Process.Start(psi); + + Send(new + { + type = "open-external-url-result", + success = true + }); + } + catch (Exception ex) + { + Send(new + { + type = "open-external-url-result", + success = false, + message = $"打开URL失败: {ex.Message}" + }); + } + } + private object? ExtractContext(JsonElement root) { if (!root.TryGetProperty("context", out var contextProperty)) diff --git a/SharpPad/wwwroot/index.html b/SharpPad/wwwroot/index.html index 35fbf2c..c210725 100644 --- a/SharpPad/wwwroot/index.html +++ b/SharpPad/wwwroot/index.html @@ -37,7 +37,7 @@
- + diff --git a/SharpPad/wwwroot/index.js b/SharpPad/wwwroot/index.js index 58cd014..51d89d9 100644 --- a/SharpPad/wwwroot/index.js +++ b/SharpPad/wwwroot/index.js @@ -6,11 +6,11 @@ const monacoConfig = { }; // 系统设置配置 -const systemSettings = { - // 禁用GPT补全 - disableGptComplete: false -}; - +const systemSettings = { + // 禁用GPT补全 + disableGptComplete: false +}; + // 获取系统设置 export function getSystemSettings() { return systemSettings; @@ -58,87 +58,90 @@ import { FileManager } from './fileSystem/fileManager.js'; import { registerCsharpProvider } from './csharpLanguageProvider.js'; import { CodeRunner } from './execution/runner.js'; import { OutputPanel } from './execution/outputPanel.js'; -import { sendRequest } from './utils/apiService.js'; -import { showNotification } from './utils/common.js'; -import { NugetManager } from './components/nuget/nugetManager.js'; -import { setupSemanticColoring } from './semanticColoring.js'; -import desktopBridge from './utils/desktopBridge.js'; +import { sendRequest } from './utils/apiService.js'; +import { showNotification } from './utils/common.js'; +import { NugetManager } from './components/nuget/nugetManager.js'; +import { setupSemanticColoring } from './semanticColoring.js'; +import desktopBridge from './utils/desktopBridge.js'; // 初始化应用 -async function initializeApp() { - // 初始化系统设置 - loadSystemSettings(); - - if (desktopBridge.isAvailable) { - desktopBridge.onceHostReady(() => { - console.log('Desktop bridge ready'); - desktopBridge.send({ type: 'ping' }); - }); - - desktopBridge.onMessage(message => { - if (!message?.type) { - return; - } - - switch (message.type) { - case 'bridge-error': - if (message.message) { - showNotification(`桌面通信错误: ${message.message}`, 'error'); - } - console.error('Desktop bridge error:', message); - break; - case 'bridge-warning': - console.warn('Desktop bridge warning:', message.message); - break; - case 'pick-and-upload-progress': - console.log('Desktop upload in progress...', message.status); - break; - case 'pick-and-upload-completed': { - const handled = typeof window.fileManager?.handleDesktopUpload === 'function' - ? window.fileManager.handleDesktopUpload(message) - : false; - - if (!handled) { - if (message.success) { - showNotification('文件上传成功', 'success'); - } else if (message.cancelled) { - showNotification('已取消上传', 'info'); - } else { - const error = message.message || '上传失败,请重试'; - showNotification(error, 'error'); - } - } - break; - } - case 'download-file-completed': { - const handled = typeof window.fileManager?.handleDesktopDownload === 'function' - ? window.fileManager.handleDesktopDownload(message) - : false; - - if (!handled) { - if (message.success) { - showNotification('文件导出成功', 'success'); - } else if (message.cancelled) { - showNotification('已取消导出', 'info'); - } else { - const error = message.message || '导出失败,请重试'; - showNotification(error, 'error'); - } - } - break; - } - case 'pong': - console.log('Desktop bridge handshake completed.'); - break; - default: - break; - } - }); - - window.requestDesktopUpload = (endpoint, context) => desktopBridge.requestPickAndUpload(endpoint, context); - } else { - console.log('Desktop bridge unavailable - running in browser mode.'); - } +async function initializeApp() { + // 初始化系统设置 + loadSystemSettings(); + + if (desktopBridge.isAvailable) { + desktopBridge.onceHostReady(() => { + console.log('Desktop bridge ready'); + desktopBridge.send({ type: 'ping' }); + + // Setup GitHub link to open in system browser + setupGitHubLinkHandler(); + }); + + desktopBridge.onMessage(message => { + if (!message?.type) { + return; + } + + switch (message.type) { + case 'bridge-error': + if (message.message) { + showNotification(`桌面通信错误: ${message.message}`, 'error'); + } + console.error('Desktop bridge error:', message); + break; + case 'bridge-warning': + console.warn('Desktop bridge warning:', message.message); + break; + case 'pick-and-upload-progress': + console.log('Desktop upload in progress...', message.status); + break; + case 'pick-and-upload-completed': { + const handled = typeof window.fileManager?.handleDesktopUpload === 'function' + ? window.fileManager.handleDesktopUpload(message) + : false; + + if (!handled) { + if (message.success) { + showNotification('文件上传成功', 'success'); + } else if (message.cancelled) { + showNotification('已取消上传', 'info'); + } else { + const error = message.message || '上传失败,请重试'; + showNotification(error, 'error'); + } + } + break; + } + case 'download-file-completed': { + const handled = typeof window.fileManager?.handleDesktopDownload === 'function' + ? window.fileManager.handleDesktopDownload(message) + : false; + + if (!handled) { + if (message.success) { + showNotification('文件导出成功', 'success'); + } else if (message.cancelled) { + showNotification('已取消导出', 'info'); + } else { + const error = message.message || '导出失败,请重试'; + showNotification(error, 'error'); + } + } + break; + } + case 'pong': + console.log('Desktop bridge handshake completed.'); + break; + default: + break; + } + }); + + window.requestDesktopUpload = (endpoint, context) => desktopBridge.requestPickAndUpload(endpoint, context); + } else { + console.log('Desktop bridge unavailable - running in browser mode.'); + } // 初始化文件系统 const fileManager = new FileManager(); @@ -294,11 +297,11 @@ async function initializeApp() { }); // 将编辑器实例暴露给全局,以便其他模块使用 - window.editor = editor; - window.editorInstance = editorInstance; - // 暴露 CodeActionProvider 用于调试 - window.editor.codeActionProvider = editorInstance.codeActionProvider; -} + window.editor = editor; + window.editorInstance = editorInstance; + // 暴露 CodeActionProvider 用于调试 + window.editor.codeActionProvider = editorInstance.codeActionProvider; +} // 从 localStorage 加载系统设置 function loadSystemSettings() { @@ -313,6 +316,22 @@ function loadSystemSettings() { } } +// 设置GitHub链接在桌面应用中使用系统浏览器打开 +function setupGitHubLinkHandler() { + const githubLink = document.getElementById('githubLink'); + if (!githubLink) { + return; + } + + githubLink.addEventListener('click', (event) => { + event.preventDefault(); + const url = githubLink.getAttribute('href'); + if (url && desktopBridge.isAvailable) { + desktopBridge.openExternalUrl(url); + } + }); +} + // 确保在DOM完全加载后再启动应用 function ensureDOMReady() { return new Promise((resolve) => { diff --git a/SharpPad/wwwroot/utils/desktopBridge.js b/SharpPad/wwwroot/utils/desktopBridge.js index 3b7d86a..83724b9 100644 --- a/SharpPad/wwwroot/utils/desktopBridge.js +++ b/SharpPad/wwwroot/utils/desktopBridge.js @@ -153,6 +153,23 @@ const desktopBridge = { return false; } + const posted = sendToHost(payload); + if (!posted) { + console.warn('Desktop bridge: 当前环境不支持宿主消息通道。'); + } + return posted; + }, + openExternalUrl(url) { + if (typeof url !== 'string' || url.trim().length === 0) { + console.warn('Desktop bridge: 缺少有效的URL。'); + return false; + } + + const payload = { + type: 'open-external-url', + url: url.trim() + }; + const posted = sendToHost(payload); if (!posted) { console.warn('Desktop bridge: 当前环境不支持宿主消息通道。');