refactor: renderer.js拆分为6个模块(core/tabs/editor/search/sidebar/markdown)
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
// 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 = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>';
|
||||
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 = '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"></polyline></svg>';
|
||||
item.appendChild(arrow);
|
||||
var icon = document.createElement('span');
|
||||
icon.className = 'tree-icon';
|
||||
icon.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>';
|
||||
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 = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>';
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user