UNPKG

browser-plugin-creator

Version:

A modern scaffolding tool for creating browser extensions with ease

330 lines (267 loc) 10.7 kB
// 侧边栏管理器 class SidePanelManager { constructor() { this.currentTab = 'overview'; this.currentStorage = 'local'; this.performanceData = []; this.init(); } async init() { this.setupEventListeners(); await this.loadCurrentTabData(); this.startAutoRefresh(); } setupEventListeners() { // 导航切换 document.querySelectorAll('.nav-item').forEach(btn => { btn.addEventListener('click', (e) => this.switchTab(e.target.dataset.section)); }); // 存储标签切换 document.querySelectorAll('.storage-tab').forEach(btn => { btn.addEventListener('click', (e) => this.switchStorage(e.target.dataset.storage)); }); // 快速操作按钮 document.getElementById('analyze-btn').addEventListener('click', () => this.analyzePage()); document.getElementById('export-btn').addEventListener('click', () => this.exportReport()); document.getElementById('screenshot-btn').addEventListener('click', () => this.takeScreenshot()); document.getElementById('refresh-btn').addEventListener('click', () => this.refreshData()); document.getElementById('settings-btn').addEventListener('click', () => this.openSettings()); // 搜索功能 document.getElementById('element-search').addEventListener('input', (e) => this.filterElements(e.target.value)); // 监听标签页变化 chrome.tabs.onActivated.addListener(() => this.loadCurrentTabData()); chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { if (changeInfo.status === 'complete') { this.loadCurrentTabData(); } }); } async switchTab(tabName) { this.currentTab = tabName; // 更新导航状态 document.querySelectorAll('.nav-item').forEach(btn => btn.classList.remove('active')); document.querySelector(`[data-section="${tabName}"]`).classList.add('active'); // 更新内容显示 document.querySelectorAll('.content-section').forEach(section => section.classList.remove('active')); document.getElementById(`${tabName}-section`).classList.add('active'); // 加载对应数据 await this.loadTabData(tabName); } async loadCurrentTabData() { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (tab && tab.id) { await this.loadTabData(this.currentTab, tab.id); } } async loadTabData(tabName, tabId = null) { this.showLoading(tabName); try { switch (tabName) { case 'overview': await this.loadOverviewData(tabId); break; case 'elements': await this.loadElementsData(tabId); break; case 'performance': await this.loadPerformanceData(tabId); break; case 'network': await this.loadNetworkData(tabId); break; case 'storage': await this.loadStorageData(tabId); break; } this.updateStatus('数据已更新'); } catch (error) { console.error(`加载${tabName}数据失败:`, error); this.updateStatus('加载失败'); } } async loadOverviewData(tabId) { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) return; const results = await chrome.tabs.sendMessage(tab.id, { type: 'GET_PAGE_OVERVIEW' }); document.getElementById('total-elements').textContent = results.totalElements || 0; document.getElementById('unique-tags').textContent = results.uniqueTags || 0; document.getElementById('color-count').textContent = results.colorCount || 0; document.getElementById('page-size').textContent = this.formatBytes(results.pageSize || 0); } async loadElementsData(tabId) { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) return; const treeData = await chrome.tabs.sendMessage(tab.id, { type: 'GET_DOM_TREE' }); this.renderElementTree(treeData); } async loadPerformanceData(tabId) { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) return; const metrics = await chrome.tabs.sendMessage(tab.id, { type: 'GET_PERFORMANCE_METRICS' }); document.getElementById('load-time').textContent = `${metrics.loadTime}ms`; document.getElementById('dom-ready-time').textContent = `${metrics.domReadyTime}ms`; document.getElementById('fcp-time').textContent = `${metrics.fcpTime}ms`; document.getElementById('lcp-time').textContent = `${metrics.lcpTime}ms`; this.renderPerformanceChart(metrics); } async loadNetworkData(tabId) { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) return; const networkData = await chrome.tabs.sendMessage(tab.id, { type: 'GET_NETWORK_DATA' }); document.getElementById('total-requests').textContent = networkData.totalRequests || 0; document.getElementById('total-size').textContent = this.formatBytes(networkData.totalSize || 0); document.getElementById('network-time').textContent = `${networkData.totalTime}ms`; this.renderResourceList(networkData.resources); } async loadStorageData(tabId) { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) return; const storageData = await chrome.tabs.sendMessage(tab.id, { type: 'GET_STORAGE_DATA', storageType: this.currentStorage }); this.renderStorageData(storageData); } renderElementTree(treeData) { const container = document.getElementById('tree-container'); container.innerHTML = this.createTreeHTML(treeData); } createTreeHTML(node, level = 0) { const indent = ' '.repeat(level); let html = `<div class="tree-node" style="margin-left: ${level * 20}px"> <span class="tree-tag">&lt;${node.tag}&gt;</span> ${node.id ? `<span class="tree-id">#${node.id}</span>` : ''} ${node.className ? `<span class="tree-class">.${node.className}</span>` : ''} </div>`; if (node.children) { node.children.forEach(child => { html += this.createTreeHTML(child, level + 1); }); } return html; } renderPerformanceChart(metrics) { const canvas = document.getElementById('performance-chart'); const ctx = canvas.getContext('2d'); // 简单的柱状图 const data = [ metrics.fcpTime, metrics.lcpTime, metrics.domReadyTime, metrics.loadTime ]; const labels = ['FCP', 'LCP', 'DOM', 'Load']; ctx.clearRect(0, 0, canvas.width, canvas.height); const barWidth = 60; const barSpacing = 20; const maxValue = Math.max(...data); data.forEach((value, index) => { const barHeight = (value / maxValue) * 150; const x = index * (barWidth + barSpacing) + 10; const y = 180 - barHeight; ctx.fillStyle = '#667eea'; ctx.fillRect(x, y, barWidth, barHeight); ctx.fillStyle = '#333'; ctx.font = '12px Arial'; ctx.textAlign = 'center'; ctx.fillText(labels[index], x + barWidth/2, 195); ctx.fillText(`${value}ms`, x + barWidth/2, y - 5); }); } renderResourceList(resources) { const container = document.getElementById('resources-container'); container.innerHTML = resources.map(resource => ` <div class="resource-item"> <div class="resource-name">${resource.name}</div> <div class="resource-size">${this.formatBytes(resource.size)}</div> <div class="resource-time">${resource.time}ms</div> </div> `).join(''); } renderStorageData(data) { const container = document.getElementById('storage-data'); container.innerHTML = Object.keys(data).map(key => ` <div class="storage-item"> <div class="storage-key">${key}</div> <div class="storage-value">${JSON.stringify(data[key])}</div> </div> `).join(''); } switchStorage(storageType) { this.currentStorage = storageType; document.querySelectorAll('.storage-tab').forEach(btn => btn.classList.remove('active')); document.querySelector(`[data-storage="${storageType}"]`).classList.add('active'); this.loadStorageData(); } async analyzePage() { this.updateStatus('分析中...'); await this.loadCurrentTabData(); } async exportReport() { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); const report = { url: tab.url, title: tab.title, timestamp: new Date().toISOString(), overview: await chrome.tabs.sendMessage(tab.id, { type: 'GET_PAGE_OVERVIEW' }), performance: await chrome.tabs.sendMessage(tab.id, { type: 'GET_PERFORMANCE_METRICS' }) }; const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `page-report-${Date.now()}.json`; a.click(); URL.revokeObjectURL(url); } async takeScreenshot() { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); const dataUrl = await chrome.tabs.captureVisibleTab(); const blob = await (await fetch(dataUrl)).blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `screenshot-${Date.now()}.png`; a.click(); URL.revokeObjectURL(url); } refreshData() { this.loadCurrentTabData(); } openSettings() { chrome.runtime.openOptionsPage(); } filterElements(searchTerm) { const treeNodes = document.querySelectorAll('.tree-node'); treeNodes.forEach(node => { const text = node.textContent.toLowerCase(); node.style.display = text.includes(searchTerm.toLowerCase()) ? 'block' : 'none'; }); } showLoading(section) { const container = document.getElementById(`${section}-section`); if (container) { container.innerHTML = '<div class="loading">加载中...</div>'; } } updateStatus(text) { document.getElementById('status-text').textContent = text; document.getElementById('last-update').textContent = new Date().toLocaleTimeString(); } startAutoRefresh() { setInterval(() => { if (document.visibilityState === 'visible') { this.loadCurrentTabData(); } }, 5000); } formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } } // 初始化侧边栏 new SidePanelManager();