From eff04606bd4ccb2f0c83ca67c8b5fd730d9a4bed Mon Sep 17 00:00:00 2001 From: thzxx Date: Thu, 21 May 2026 12:56:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BE=A7=E8=BE=B9=E6=A0=8F=E9=AB=98?= =?UTF-8?q?=E4=BA=AE=E8=B7=9F=E9=9A=8F=E6=A0=87=E7=AD=BE=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=8B=AC=E7=AB=8B=E6=89=93=E5=BC=80=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 updateSidebarHighlight() 函数,标签切换时只更新高亮状态而非重建整棵树 - 给树节点添加 data-path 属性用于精确定位高亮 - 新增「已打开的文件」区域,显示不在当前文件夹内的独立文件 - 独立文件支持点击切换、修改状态标识、活跃高亮 - 无文件夹打开时自动显示独立文件列表,全部关闭后隐藏侧边栏 --- renderer/renderer.js | 111 ++++++++++++++++++++++++++++++++++++++++++- renderer/style.css | 22 +++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/renderer/renderer.js b/renderer/renderer.js index 60e79c5..2f739db 100644 --- a/renderer/renderer.js +++ b/renderer/renderer.js @@ -539,6 +539,36 @@ } } + // ===== Sidebar Highlight ===== + // Update sidebar active highlight without re-rendering the entire tree + function updateSidebarHighlight() { + const tab = getActiveTab(); + const activePath = tab ? tab.filePath : null; + + // If no folder is open, just re-render independent files + if (!currentRootPath) { + sidebarTree.innerHTML = ''; + const indepSection = renderIndependentFiles(); + if (indepSection) sidebarTree.appendChild(indepSection); + return; + } + + // Re-render independent files section (files may have been added/removed) + const oldSection = sidebarTree.querySelector('.independent-files-section'); + const newSection = renderIndependentFiles(); + if (oldSection) oldSection.remove(); + if (newSection) sidebarTree.insertBefore(newSection, sidebarTree.firstChild); + + // Update folder tree items + const treeItems = sidebarTree.querySelectorAll('.tree-item:not(.independent-file-item)'); + treeItems.forEach(item => { + const itemPath = item.dataset.path; + if (itemPath) { + item.classList.toggle('active', itemPath === activePath); + } + }); + } + // ===== Tab Operations ===== function getActiveTab() { return tabs.find(t => t.id === activeTabId) || null; @@ -589,6 +619,15 @@ // Update modified state setModified(tab.isModified); + // Update sidebar highlight and independent files + updateSidebarHighlight(); + // If no folder is open but we have open files, ensure sidebar is visible + if (!currentRootPath && tabs.length > 0) { + sidebar.classList.remove('collapsed'); + } else if (!currentRootPath && tabs.length === 0) { + sidebar.classList.add('collapsed'); + } + // Notify main process about active file if (typeof window.electronAPI !== 'undefined') { window.electronAPI.tabSwitched(tab.filePath); @@ -638,6 +677,7 @@ loadTabState(tab); renderTabBar(); updateTitle(); + updateSidebarHighlight(); welcomeScreen.classList.add('hidden'); } @@ -684,6 +724,7 @@ renderTabBar(); updateTitle(); + updateSidebarHighlight(); } // ===== File Size Limit ===== @@ -1379,6 +1420,7 @@ if (tab && tab.filePath === node.path) { item.classList.add('active'); } + item.dataset.path = node.path; parent.appendChild(item); @@ -1407,11 +1449,78 @@ return lastTreeData || []; } - // Override renderTree to cache data + // Build list of open files that are NOT inside currentRootPath + function getIndependentFiles() { + if (!tabs.length) return []; + return tabs.filter(t => { + if (!t.filePath) return false; // skip unnamed tabs + if (!currentRootPath) return true; // no folder open → all files are "independent" + // Check if file is inside the current root path + return !t.filePath.startsWith(currentRootPath); + }); + } + + function renderIndependentFiles() { + const files = getIndependentFiles(); + if (files.length === 0) return null; + + const section = document.createElement('div'); + section.className = 'independent-files-section'; + + const header = document.createElement('div'); + header.className = 'independent-files-header'; + header.textContent = '已打开的文件'; + section.appendChild(header); + + for (const tab of files) { + const item = document.createElement('div'); + item.className = 'tree-item independent-file-item'; + item.style.paddingLeft = '8px'; + item.dataset.path = tab.filePath; + + const icon = document.createElement('span'); + icon.className = 'tree-icon'; + icon.innerHTML = ''; + item.appendChild(icon); + + const name = document.createElement('span'); + name.className = 'tree-name'; + name.textContent = getFileName(tab.filePath); + item.appendChild(name); + + // Mark as modified + if (tab.isModified) { + const dot = document.createElement('span'); + dot.className = 'independent-modified-dot'; + dot.textContent = ' •'; + item.appendChild(dot); + } + + // Highlight if active + if (tab.id === activeTabId) { + item.classList.add('active'); + } + + item.addEventListener('click', (e) => { + e.stopPropagation(); + switchToTab(tab.id); + }); + + section.appendChild(item); + } + return section; + } + + // Override renderTree to cache data and prepend independent files const _originalRenderTree = renderTree; renderTree = function(tree, rootPath) { lastTreeData = tree; _originalRenderTree(tree, rootPath); + // Prepend independent files section + const indepSection = renderIndependentFiles(); + if (indepSection) { + sidebarTree.insertBefore(indepSection, sidebarTree.firstChild); + } }; // ===== Search Bar Init ===== diff --git a/renderer/style.css b/renderer/style.css index 884d229..6187e4c 100644 --- a/renderer/style.css +++ b/renderer/style.css @@ -860,6 +860,28 @@ html, body { text-overflow: ellipsis; } +/* ===== Independent Files Section ===== */ +.independent-files-section { + border-bottom: 1px solid var(--border); + margin-bottom: 4px; + padding-bottom: 4px; +} + +.independent-files-header { + font-size: 11px; + font-weight: 600; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + padding: 8px 12px 4px 12px; +} + +.independent-file-item .independent-modified-dot { + color: var(--primary); + font-size: 14px; + margin-left: 4px; +} + /* ===== Search & Replace Bar ===== */ #search-bar { background: var(--search-bg);