browser-plugin-creator
Version:
A modern scaffolding tool for creating browser extensions with ease
330 lines (267 loc) • 10.7 kB
JavaScript
// 侧边栏管理器
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"><${node.tag}></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();