From aac6a0b0732d5092de761676df7b35d8b477425b Mon Sep 17 00:00:00 2001 From: thzxx Date: Mon, 18 May 2026 13:55:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=A2=84=E8=A7=88=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E3=80=81=E5=85=B3=E9=97=AD=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E3=80=81=E9=93=BE=E6=8E=A5=E8=B7=B3=E8=BD=AC=E3=80=81=E7=9B=91?= =?UTF-8?q?=E5=90=AC=E5=99=A8=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 预览区无法选择复制 — #preview 添加 user-select: text 2. IPC 监听器重复注册 — 注册前先 removeAllListeners 防止堆叠 3. 关闭确认取消后 5s 超时仍强制关闭 — 新增 cancelClose IPC 清除超时 4. 预览区链接点击离开应用 — 拦截点击,通过 shell.openExternal 打开 5. 预览区链接光标样式 — .markdown-body a 添加 cursor: pointer --- main.js | 15 ++++++++++++++- preload.js | 6 +++++- renderer/renderer.js | 37 ++++++++++++++++++++++++++++++++++++- renderer/style.css | 1 + 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/main.js b/main.js index be421d0..ef3d7f7 100644 --- a/main.js +++ b/main.js @@ -6,6 +6,7 @@ let mainWindow = null; let activeFilePath = null; // Currently active tab's file path let pendingFilePath = null; let fileWatcher = null; +let closeTimeout = null; function createWindow() { mainWindow = new BrowserWindow({ @@ -48,7 +49,8 @@ function createWindow() { mainWindow.close(); return; } - setTimeout(() => { + // 5s safety timeout in case renderer is unresponsive + closeTimeout = setTimeout(() => { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.removeAllListeners('close'); mainWindow.close(); @@ -237,11 +239,22 @@ ipcMain.handle('tab:switched', (event, filePath) => { }); ipcMain.handle('window:forceClose', () => { + if (closeTimeout) { + clearTimeout(closeTimeout); + closeTimeout = null; + } stopWatching(); mainWindow.removeAllListeners('close'); mainWindow.close(); }); +ipcMain.handle('window:cancelClose', () => { + if (closeTimeout) { + clearTimeout(closeTimeout); + closeTimeout = null; + } +}); + // Command line file function getCommandLineFile() { const args = process.argv.slice(1); diff --git a/preload.js b/preload.js index 8258c3a..af52ab3 100644 --- a/preload.js +++ b/preload.js @@ -1,4 +1,4 @@ -const { contextBridge, ipcRenderer } = require('electron'); +const { contextBridge, ipcRenderer, shell } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { // File operations @@ -15,6 +15,10 @@ contextBridge.exposeInMainWorld('electronAPI', { // Window control forceClose: () => ipcRenderer.invoke('window:forceClose'), + cancelClose: () => ipcRenderer.invoke('window:cancelClose'), + + // Shell + openExternal: (url) => shell.openExternal(url), // Events from main process onFileOpenInTab: (callback) => ipcRenderer.on('file:openInTab', (event, data) => callback(data)), diff --git a/renderer/renderer.js b/renderer/renderer.js index 8b59295..b73b181 100644 --- a/renderer/renderer.js +++ b/renderer/renderer.js @@ -128,6 +128,29 @@ } } + // Intercept link clicks in preview — open in system browser + function initPreviewLinks() { + preview.addEventListener('click', (e) => { + const link = e.target.closest('a'); + if (!link) return; + e.preventDefault(); + const href = link.getAttribute('href'); + if (!href) return; + // Anchor links: scroll within preview + if (href.startsWith('#')) { + const target = preview.querySelector(href); + if (target) target.scrollIntoView({ behavior: 'smooth' }); + return; + } + // External links: open in system browser + if (typeof window.electronAPI !== 'undefined' && window.electronAPI.openExternal) { + window.electronAPI.openExternal(href); + } else { + window.open(href, '_blank', 'noopener'); + } + }); + } + // ===== Line Numbers ===== function updateLineNumbers() { const count = editor.value.split('\n').length; @@ -663,7 +686,11 @@ return; } const shouldClose = confirm('有文件尚未保存,确定要关闭吗?'); - if (shouldClose) window.electronAPI.forceClose(); + if (shouldClose) { + window.electronAPI.forceClose(); + } else { + window.electronAPI.cancelClose(); + } }); } @@ -695,6 +722,13 @@ btnWelcomeNew.addEventListener('click', () => createNewTab(null, '')); if (typeof window.electronAPI !== 'undefined') { + // Remove old listeners first to prevent stacking + window.electronAPI.removeAllListeners('file:openInTab'); + window.electronAPI.removeAllListeners('file:opened'); + window.electronAPI.removeAllListeners('menu:save'); + window.electronAPI.removeAllListeners('menu:saveAs'); + window.electronAPI.removeAllListeners('menu:viewMode'); + // Main process sends file to open in a new tab window.electronAPI.onFileOpenInTab((data) => { createNewTab(data.filePath, data.content); @@ -719,6 +753,7 @@ initDragDrop(); initKeyboard(); initEditorTab(); + initPreviewLinks(); initEventListeners(); initFileWatch(); initUnsavedWarning(); diff --git a/renderer/style.css b/renderer/style.css index 5b4a36e..aa8bac4 100644 --- a/renderer/style.css +++ b/renderer/style.css @@ -431,6 +431,7 @@ html, body { .markdown-body a { color: var(--primary); text-decoration: none; + cursor: pointer; } .markdown-body a:hover {