// MarkLite — Sidebar Module: file tree, independent files, resize handle (function () { 'use strict'; var ML = window.MarkLite; var expandedDirs = new Set(); var lastTreeData = null; // ===== Sidebar Highlight ===== ML.updateSidebarHighlight = function () { var tab = ML.getActiveTab(); var activePath = tab ? tab.filePath : null; if (!ML.currentRootPath) { ML.sidebarTree.innerHTML = ''; var indep = renderIndependentFiles(); if (indep) ML.sidebarTree.appendChild(indep); return; } var oldSection = ML.sidebarTree.querySelector('.independent-files-section'); var newSection = renderIndependentFiles(); if (oldSection) oldSection.remove(); if (newSection) ML.sidebarTree.insertBefore(newSection, ML.sidebarTree.firstChild); var treeItems = ML.sidebarTree.querySelectorAll('.tree-item:not(.independent-file-item)'); treeItems.forEach(function (item) { var itemPath = item.dataset.path; if (itemPath) item.classList.toggle('active', itemPath === activePath); }); }; // ===== Independent Files ===== function getIndependentFiles() { if (!ML.tabs.length) return []; return ML.tabs.filter(function (t) { if (!t.filePath) return false; if (!ML.currentRootPath) return true; return !t.filePath.startsWith(ML.currentRootPath); }); } function renderIndependentFiles() { var files = getIndependentFiles(); if (files.length === 0) return null; var section = document.createElement('div'); section.className = 'independent-files-section'; var header = document.createElement('div'); header.className = 'independent-files-header'; header.textContent = '已打开的文件'; section.appendChild(header); files.forEach(function (tab) { var item = document.createElement('div'); item.className = 'tree-item independent-file-item'; item.style.paddingLeft = '8px'; item.dataset.path = tab.filePath; var icon = document.createElement('span'); icon.className = 'tree-icon'; icon.innerHTML = ''; item.appendChild(icon); var name = document.createElement('span'); name.className = 'tree-name'; name.textContent = ML.getFileName(tab.filePath); item.appendChild(name); if (tab.isModified) { var dot = document.createElement('span'); dot.className = 'independent-modified-dot'; dot.textContent = ' •'; item.appendChild(dot); } if (tab.id === ML.activeTabId) item.classList.add('active'); item.addEventListener('click', function (e) { e.stopPropagation(); ML.switchToTab(tab.id); }); section.appendChild(item); }); return section; } // ===== Folder Operations ===== async function openFolderDialog() { if (typeof window.electronAPI !== 'undefined' && window.electronAPI.openFolderDialog) { var result = await window.electronAPI.openFolderDialog(); if (result) loadFolder(result); } } async function loadFolder(dirPath) { if (typeof window.electronAPI === 'undefined') return; ML.currentRootPath = dirPath; var result = await window.electronAPI.readDirTree(dirPath); if (result.success) { renderTree(result.tree, dirPath); ML.sidebar.classList.remove('collapsed'); window.electronAPI.watchDir(dirPath); } } async function refreshTree() { if (!ML.currentRootPath) return; var result = await window.electronAPI.readDirTree(ML.currentRootPath); if (result.success) renderTree(result.tree, ML.currentRootPath); } // ===== Tree Rendering ===== function renderTreeOriginal(tree, rootPath) { ML.sidebarTree.innerHTML = ''; var rootNode = { name: ML.getFileName(rootPath) || rootPath, path: rootPath, type: 'dir', children: tree }; expandedDirs.add(rootPath); var fragment = document.createDocumentFragment(); buildTreeDOM(fragment, rootNode, 0); ML.sidebarTree.appendChild(fragment); } function renderTree(tree, rootPath) { lastTreeData = tree; renderTreeOriginal(tree, rootPath); var folderHeader = document.createElement('div'); folderHeader.className = 'sidebar-section-header'; folderHeader.textContent = '文件夹目录树'; ML.sidebarTree.insertBefore(folderHeader, ML.sidebarTree.firstChild); var indepSection = renderIndependentFiles(); if (indepSection) ML.sidebarTree.insertBefore(indepSection, ML.sidebarTree.firstChild); } function buildTreeDOM(parent, node, depth) { var item = document.createElement('div'); item.className = 'tree-item'; item.style.paddingLeft = (8 + depth * 16) + 'px'; if (node.type === 'dir') { var isExpanded = expandedDirs.has(node.path); var arrow = document.createElement('span'); arrow.className = 'tree-arrow' + (isExpanded ? ' expanded' : ''); arrow.innerHTML = ''; item.appendChild(arrow); var icon = document.createElement('span'); icon.className = 'tree-icon'; icon.innerHTML = ''; item.appendChild(icon); var name = document.createElement('span'); name.className = 'tree-name'; name.textContent = node.name; item.appendChild(name); parent.appendChild(item); if (isExpanded && node.children) { node.children.forEach(function (child) { buildTreeDOM(parent, child, depth + 1); }); } item.addEventListener('click', function (e) { e.stopPropagation(); if (expandedDirs.has(node.path)) expandedDirs.delete(node.path); else expandedDirs.add(node.path); renderTree(lastTreeData || [], ML.currentRootPath); }); } else { var spacer = document.createElement('span'); spacer.style.width = '16px'; spacer.style.flexShrink = '0'; item.appendChild(spacer); var fileIcon = document.createElement('span'); fileIcon.className = 'tree-icon'; fileIcon.innerHTML = ''; item.appendChild(fileIcon); var fileName = document.createElement('span'); fileName.className = 'tree-name'; fileName.textContent = node.name; item.appendChild(fileName); var tab = ML.getActiveTab(); if (tab && tab.filePath === node.path) item.classList.add('active'); item.dataset.path = node.path; parent.appendChild(item); item.addEventListener('click', async function (e) { e.stopPropagation(); var existing = ML.tabs.find(function (t) { return t.filePath === node.path; }); if (existing) { ML.switchToTab(existing.id); } else if (typeof window.electronAPI !== 'undefined') { var result = await window.electronAPI.readFile(node.path); if (result.success) ML.createNewTab(node.path, result.content); } }); } } // ===== Init ===== ML.initSidebar = function () { var settings = ML.loadSettings(); if (settings.sidebarCollapsed) ML.sidebar.classList.add('collapsed'); if (settings.sidebarWidth) ML.sidebar.style.width = settings.sidebarWidth + 'px'; // Resize handle var handle = document.createElement('div'); handle.className = 'sidebar-resize-handle'; ML.sidebar.appendChild(handle); var isResizing = false; handle.addEventListener('mousedown', function (e) { isResizing = true; document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; e.preventDefault(); }); document.addEventListener('mousemove', function (e) { if (!isResizing) return; var newWidth = Math.max(180, Math.min(500, e.clientX)); ML.sidebar.style.width = newWidth + 'px'; ML.saveSetting('sidebarWidth', newWidth); }); document.addEventListener('mouseup', function () { if (isResizing) { isResizing = false; document.body.style.cursor = ''; document.body.style.userSelect = ''; } }); ML.btnOpenFolder.addEventListener('click', function () { if (typeof window.electronAPI !== 'undefined') openFolderDialog(); }); if (typeof window.electronAPI !== 'undefined') { var dirRefreshTimer = null; window.electronAPI.onDirChanged(function () { if (!ML.currentRootPath) return; if (dirRefreshTimer) clearTimeout(dirRefreshTimer); dirRefreshTimer = setTimeout(function () { refreshTree(); }, 300); }); } }; })();