From af008711345f1987cb40099cd8dca35a9ce5d34c Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 10:09:19 +0000 Subject: [PATCH 1/2] fix: handle GitHub link in desktop app with system browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added openExternalUrl() method to desktopBridge for opening URLs in system browser - Implemented open-external-url handler in WebViewBridge.cs using Process.Start() - Removed target="_blank" from GitHub link and added id for JavaScript targeting - Added setupGitHubLinkHandler() to intercept link clicks in desktop mode This ensures GitHub link opens intentionally in system browser when running in desktop app, while maintaining normal behavior in web browser. Fixes #60 Co-authored-by: 小小高 --- SharpPad.Desktop/Interop/WebViewBridge.cs | 58 ++++++- SharpPad/wwwroot/index.html | 2 +- SharpPad/wwwroot/index.js | 197 ++++++++++++---------- SharpPad/wwwroot/utils/desktopBridge.js | 17 ++ 4 files changed, 183 insertions(+), 91 deletions(-) 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: 当前环境不支持宿主消息通道。'); From 008cc43de64f3df91fb5b6494db3f7a8724a293c Mon Sep 17 00:00:00 2001 From: gaocong Date: Fri, 17 Oct 2025 18:40:48 +0800 Subject: [PATCH 2/2] fix: specify UTF8 encoding for ConsoleReadKeyShim source parsing --- MonacoRoslynCompletionProvider/Api/CodeRunner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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)